pub struct Gateways {
    pub mistercash: Option<Mistercash>,
}


impl CardNetworkTokenizeExecutor<'_, domain::TokenizeCardRequest> {
    pub async fn validate_request_and_fetch_optional_customer(
        &self,
    ) -> RouterResult<Option<api::CustomerDetails>> {
        // Validate card's expiry
        migration::validate_card_expiry(&self.data.card_expiry_month, &self.data.card_expiry_year)?;

        // Validate customer ID
        let customer_id = self
            .customer
            .customer_id
            .as_ref()
            .get_required_value("customer_id")
            .change_context(errors::ApiErrorResponse::MissingRequiredField {
                field_name: "customer.customer_id",
            })?;

        // Fetch customer details if present
        let db = &*self.state.store;
        let key_manager_state: &KeyManagerState = &self.state.into();
        db.find_customer_optional_by_customer_id_merchant_id(
            key_manager_state,
            customer_id,
            self.merchant_account.get_id(),
            self.key_store,
            self.merchant_account.storage_scheme,
        )
        .await
        .inspect_err(|err| logger::info!("Error fetching customer: {:?}", err))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .map_or(
            // Validate if customer creation is feasible
            if self.customer.name.is_some()
                || self.customer.email.is_some()
                || self.customer.phone.is_some()
            {
                Ok(None)
            } else {
                Err(report!(errors::ApiErrorResponse::MissingRequiredFields {
                    field_names: vec!["customer.name", "customer.email", "customer.phone"],
                }))
            },
            // If found, send back CustomerDetails from DB
            |optional_customer| {
                Ok(optional_customer.map(|customer| api::CustomerDetails {
                    id: customer.customer_id.clone(),
                    name: customer.name.clone().map(|name| name.into_inner()),
                    email: customer.email.clone().map(Email::from),
                    phone: customer.phone.clone().map(|phone| phone.into_inner()),
                    phone_country_code: customer.phone_country_code.clone(),
                    tax_registration_id: customer
                        .tax_registration_id
                        .clone()
                        .map(|tax_registration_id| tax_registration_id.into_inner()),
                }))
            },
        )
    }

    pub async fn create_customer(&self) -> RouterResult<api::CustomerDetails> {
        let db = &*self.state.store;
        let customer_id = self
            .customer
            .customer_id
            .as_ref()
            .get_required_value("customer_id")
            .change_context(errors::ApiErrorResponse::MissingRequiredField {
                field_name: "customer_id",
            })?;
        let key_manager_state: &KeyManagerState = &self.state.into();

        let encrypted_data = crypto_operation(
            key_manager_state,
            type_name!(domain::Customer),
            CryptoOperation::BatchEncrypt(domain::FromRequestEncryptableCustomer::to_encryptable(
                domain::FromRequestEncryptableCustomer {
                    name: self.customer.name.clone(),
                    email: self
                        .customer
                        .email
                        .clone()
                        .map(|email| email.expose().switch_strategy()),
                    phone: self.customer.phone.clone(),
                    tax_registration_id: self.customer.tax_registration_id.clone(),
                },
            )),
            Identifier::Merchant(self.merchant_account.get_id().clone()),
            self.key_store.key.get_inner().peek(),
        )
        .await
        .inspect_err(|err| logger::info!("Error encrypting customer: {:?}", err))
        .and_then(|val| val.try_into_batchoperation())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to encrypt customer")?;

        let encryptable_customer =
            domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to form EncryptableCustomer")?;

        let new_customer_id = generate_customer_id_of_default_length();
        let domain_customer = domain::Customer {
            customer_id: new_customer_id.clone(),
            merchant_id: self.merchant_account.get_id().clone(),
            name: encryptable_customer.name,
            email: encryptable_customer.email.map(|email| {
                utils::Encryptable::new(
                    email.clone().into_inner().switch_strategy(),
                    email.into_encrypted(),
                )
            }),
            phone: encryptable_customer.phone,
            description: None,
            phone_country_code: self.customer.phone_country_code.to_owned(),
            metadata: None,
            connector_customer: None,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            address_id: None,
            default_payment_method_id: None,
            updated_by: None,
            version: common_types::consts::API_VERSION,
            tax_registration_id: encryptable_customer.tax_registration_id,
        };

        db.insert_customer(
            domain_customer,
            key_manager_state,
            self.key_store,
            self.merchant_account.storage_scheme,
        )
        .await
        .inspect_err(|err| logger::info!("Error creating a customer: {:?}", err))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!(
                "Failed to insert customer [id - {:?}] for merchant [id - {:?}]",
                customer_id,
                self.merchant_account.get_id()
            )
        })?;

        Ok(api::CustomerDetails {
            id: new_customer_id,
            name: self.customer.name.clone(),
            email: self.customer.email.clone(),
            phone: self.customer.phone.clone(),
            phone_country_code: self.customer.phone_country_code.clone(),
            tax_registration_id: self.customer.tax_registration_id.clone(),
        })
    }

    pub async fn store_card_and_token_in_locker(
        &self,
        network_token: &NetworkTokenizationResponse,
        card: &domain::CardDetail,
        customer_id: &id_type::CustomerId,
    ) -> RouterResult<StoreLockerResponse> {
        let stored_card_resp = self.store_card_in_locker(card, customer_id).await?;
        let stored_token_resp = self
            .store_network_token_in_locker(
                network_token,
                customer_id,
                card.card_holder_name.clone(),
                card.nick_name.clone(),
            )
            .await?;
        let store_locker_response = StoreLockerResponse {
            store_card_resp: stored_card_resp,
            store_token_resp: stored_token_resp,
        };
        Ok(store_locker_response)
    }

    pub async fn store_card_in_locker(
        &self,
        card: &domain::CardDetail,
        customer_id: &id_type::CustomerId,
    ) -> RouterResult<pm_transformers::StoreCardRespPayload> {
        let merchant_id = self.merchant_account.get_id();
        let locker_req =
            pm_transformers::StoreLockerReq::LockerCard(pm_transformers::StoreCardReq {
                merchant_id: merchant_id.clone(),
                merchant_customer_id: customer_id.clone(),
                card: payment_methods_api::Card {
                    card_number: card.card_number.clone(),
                    card_exp_month: card.card_exp_month.clone(),
                    card_exp_year: card.card_exp_year.clone(),
                    card_isin: Some(card.card_number.get_card_isin().clone()),
                    name_on_card: card.card_holder_name.clone(),
                    nick_name: card
                        .nick_name
                        .as_ref()
                        .map(|nick_name| nick_name.clone().expose()),
                    card_brand: None,
                },
                requestor_card_reference: None,
                ttl: self.state.conf.locker.ttl_for_storage_in_secs,
            });

        let stored_resp = add_card_to_hs_locker(
            self.state,
            &locker_req,
            customer_id,
            api_enums::LockerChoice::HyperswitchCardVault,
        )
        .await
        .inspect_err(|err| logger::info!("Error adding card in locker: {:?}", err))
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

        Ok(stored_resp)
    }

    pub async fn create_payment_method(
        &self,
        stored_locker_resp: &StoreLockerResponse,
        network_token_details: &NetworkTokenizationResponse,
        card_details: &domain::CardDetail,
        customer_id: &id_type::CustomerId,
    ) -> RouterResult<domain::PaymentMethod> {
        let payment_method_id = common_utils::generate_id(consts::ID_LENGTH, "pm");

        // Form encrypted PM data (original card)
        let enc_pm_data = self.encrypt_card(card_details, true).await?;

        // Form encrypted network token data
        let enc_token_data = self
            .encrypt_network_token(network_token_details, card_details, true)
            .await?;

        // Form PM create entry
        let payment_method_create = api::PaymentMethodCreate {
            payment_method: Some(api_enums::PaymentMethod::Card),
            payment_method_type: card_details
                .card_type
                .as_ref()
                .and_then(|card_type| api_enums::PaymentMethodType::from_str(card_type).ok()),
            payment_method_issuer: card_details.card_issuer.clone(),
            payment_method_issuer_code: None,
            card: Some(api::CardDetail {
                card_number: card_details.card_number.clone(),
                card_exp_month: card_details.card_exp_month.clone(),
                card_exp_year: card_details.card_exp_year.clone(),
                card_holder_name: card_details.card_holder_name.clone(),
                nick_name: card_details.nick_name.clone(),
                card_issuing_country: card_details.card_issuing_country.clone(),
                card_network: card_details.card_network.clone(),
                card_issuer: card_details.card_issuer.clone(),
                card_type: card_details.card_type.clone(),
            }),
            metadata: None,
            customer_id: Some(customer_id.clone()),
            card_network: card_details
                .card_network
                .as_ref()
                .map(|network| network.to_string()),
            bank_transfer: None,
            wallet: None,
            client_secret: None,
            payment_method_data: None,
            billing: None,
            connector_mandate_details: None,
            network_transaction_id: None,
        };
        PmCards {
            state: self.state,
            merchant_context: &domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
                self.merchant_account.clone(),
                self.key_store.clone(),
            ))),
        }
        .create_payment_method(
            &payment_method_create,
            customer_id,
            &payment_method_id,
            Some(stored_locker_resp.store_card_resp.card_reference.clone()),
            self.merchant_account.get_id(),
            None,
            None,
            Some(enc_pm_data),
            None,
            None,
            None,
            None,
            None,
            network_token_details.1.clone(),
            Some(stored_locker_resp.store_token_resp.card_reference.clone()),
            Some(enc_token_data),
        )
        .await
    }
}


OpenAPI Block Path: components.schemas.GpayAllowedMethodsParameters
{
  "type": "object",
  "required": [
    "allowed_auth_methods",
    "allowed_card_networks"
  ],
  "properties": {
    "allowed_auth_methods": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "The list of allowed auth methods (ex: 3DS, No3DS, PAN_ONLY etc)"
    },
    "allowed_card_networks": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "The list of allowed card networks (ex: AMEX,JCB etc)"
    },
    "billing_address_required": {
      "type": "boolean",
      "description": "Is billing address required",
      "nullable": true
    },
    "billing_address_parameters": {
      "allOf": [
        {
          "$ref": "#/components/schemas/GpayBillingAddressParameters"
        }
      ],
      "nullable": true
    },
    "assurance_details_required": {
      "type": "boolean",
      "description": "Whether assurance details are required",
      "nullable": true
    }
  }
}


pub struct Zen;


pub struct MandatePaymentMethodData {
    pub entry_mode: PaymentMethodEntryMode,
    pub id: Option<String>,
}


pub struct AuthorizationInfoResponse(pub Vec<AuthorizationInfo>);


impl api::revenue_recovery::RevenueRecoveryRecordBack for Chargebee {}

impl api::revenue_recovery::RevenueRecoveryRecordBack for for Chargebee


pub async fn construct_public_and_private_db_configs(
    state: &SessionState,
    auth_config: &user_api::AuthConfig,
    encryption_key: &[u8],
    id: String,
) -> UserResult<(Option<Encryption>, Option<serde_json::Value>)> {
    match auth_config {
        user_api::AuthConfig::OpenIdConnect {
            private_config,
            public_config,
        } => {
            let private_config_value = serde_json::to_value(private_config.clone())
                .change_context(UserErrors::InternalServerError)
                .attach_printable("Failed to convert auth config to json")?;

            let encrypted_config =
                domain::types::crypto_operation::<serde_json::Value, masking::WithType>(
                    &state.into(),
                    type_name!(diesel_models::user::User),
                    domain::types::CryptoOperation::Encrypt(private_config_value.into()),
                    Identifier::UserAuth(id),
                    encryption_key,
                )
                .await
                .and_then(|val| val.try_into_operation())
                .change_context(UserErrors::InternalServerError)
                .attach_printable("Failed to encrypt auth config")?;

            Ok((
                Some(encrypted_config.into()),
                Some(
                    serde_json::to_value(public_config.clone())
                        .change_context(UserErrors::InternalServerError)
                        .attach_printable("Failed to convert auth config to json")?,
                ),
            ))
        }
        user_api::AuthConfig::Password | user_api::AuthConfig::MagicLink => Ok((None, None)),
    }
}


File: crates/redis_interface/src/types.rs
 Public functions: 10
 Public structs: 3

//! Data types and type conversions
//! from `fred`'s internal data-types to custom data-types

use common_utils::errors::CustomResult;
use fred::types::RedisValue as FredRedisValue;

use crate::{errors, RedisConnectionPool};

pub struct RedisValue {
    inner: FredRedisValue,
}

impl std::ops::Deref for RedisValue {
    type Target = FredRedisValue;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl RedisValue {
    pub fn new(value: FredRedisValue) -> Self {
        Self { inner: value }
    }
    pub fn into_inner(self) -> FredRedisValue {
        self.inner
    }

    pub fn from_bytes(val: Vec<u8>) -> Self {
        Self {
            inner: FredRedisValue::Bytes(val.into()),
        }
    }
    pub fn from_string(value: String) -> Self {
        Self {
            inner: FredRedisValue::String(value.into()),
        }
    }
}

impl From<RedisValue> for FredRedisValue {
    fn from(v: RedisValue) -> Self {
        v.inner
    }
}

#[derive(Debug, serde::Deserialize, Clone)]
#[serde(default)]
pub struct RedisSettings {
    pub host: String,
    pub port: u16,
    pub cluster_enabled: bool,
    pub cluster_urls: Vec<String>,
    pub use_legacy_version: bool,
    pub pool_size: usize,
    pub reconnect_max_attempts: u32,
    /// Reconnect delay in milliseconds
    pub reconnect_delay: u32,
    /// TTL in seconds
    pub default_ttl: u32,
    /// TTL for hash-tables in seconds
    pub default_hash_ttl: u32,
    pub stream_read_count: u64,
    pub auto_pipeline: bool,
    pub disable_auto_backpressure: bool,
    pub max_in_flight_commands: u64,
    pub default_command_timeout: u64,
    pub max_feed_count: u64,
    pub unresponsive_timeout: u64,
}

impl RedisSettings {
    /// Validates the Redis configuration provided.
    pub fn validate(&self) -> CustomResult<(), errors::RedisError> {
        use common_utils::{ext_traits::ConfigExt, fp_utils::when};

        when(self.host.is_default_or_empty(), || {
            Err(errors::RedisError::InvalidConfiguration(
                "Redis `host` must be specified".into(),
            ))
        })?;

        when(self.cluster_enabled && self.cluster_urls.is_empty(), || {
            Err(errors::RedisError::InvalidConfiguration(
                "Redis `cluster_urls` must be specified if `cluster_enabled` is `true`".into(),
            ))
        })?;

        when(
            self.default_command_timeout < self.unresponsive_timeout,
            || {
                Err(errors::RedisError::InvalidConfiguration(
                    "Unresponsive timeout cannot be greater than the command timeout".into(),
                )
                .into())
            },
        )
    }
}

impl Default for RedisSettings {
    fn default() -> Self {
        Self {
            host: "127.0.0.1".to_string(),
            port: 6379,
            cluster_enabled: false,
            cluster_urls: vec![],
            use_legacy_version: false,
            pool_size: 5,
            reconnect_max_attempts: 5,
            reconnect_delay: 5,
            default_ttl: 300,
            stream_read_count: 1,
            default_hash_ttl: 900,
            auto_pipeline: true,
            disable_auto_backpressure: false,
            max_in_flight_commands: 5000,
            default_command_timeout: 30,
            max_feed_count: 200,
            unresponsive_timeout: 10,
        }
    }
}

#[derive(Debug)]
pub enum RedisEntryId {
    UserSpecifiedID {
        milliseconds: String,
        sequence_number: String,
    },
    AutoGeneratedID,
    AfterLastID,
    /// Applicable only with consumer groups
    UndeliveredEntryID,
}

impl From<RedisEntryId> for fred::types::XID {
    fn from(id: RedisEntryId) -> Self {
        match id {
            RedisEntryId::UserSpecifiedID {
                milliseconds,
                sequence_number,
            } => Self::Manual(fred::bytes_utils::format_bytes!(
                "{milliseconds}-{sequence_number}"
            )),
            RedisEntryId::AutoGeneratedID => Self::Auto,
            RedisEntryId::AfterLastID => Self::Max,
            RedisEntryId::UndeliveredEntryID => Self::NewInGroup,
        }
    }
}

impl From<&RedisEntryId> for fred::types::XID {
    fn from(id: &RedisEntryId) -> Self {
        match id {
            RedisEntryId::UserSpecifiedID {
                milliseconds,
                sequence_number,
            } => Self::Manual(fred::bytes_utils::format_bytes!(
                "{milliseconds}-{sequence_number}"
            )),
            RedisEntryId::AutoGeneratedID => Self::Auto,
            RedisEntryId::AfterLastID => Self::Max,
            RedisEntryId::UndeliveredEntryID => Self::NewInGroup,
        }
    }
}

#[derive(Eq, PartialEq)]
pub enum SetnxReply {
    KeySet,
    KeyNotSet, // Existing key
}

impl fred::types::FromRedis for SetnxReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            // Returns String ( "OK" ) in case of success
            fred::types::RedisValue::String(_) => Ok(Self::KeySet),
            // Return Null in case of failure
            fred::types::RedisValue::Null => Ok(Self::KeyNotSet),
            // Unexpected behaviour
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected SETNX command reply",
            )),
        }
    }
}

#[derive(Eq, PartialEq)]
pub enum HsetnxReply {
    KeySet,
    KeyNotSet, // Existing key
}

impl fred::types::FromRedis for HsetnxReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::Integer(1) => Ok(Self::KeySet),
            fred::types::RedisValue::Integer(0) => Ok(Self::KeyNotSet),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected HSETNX command reply",
            )),
        }
    }
}

#[derive(Eq, PartialEq)]
pub enum MsetnxReply {
    KeysSet,
    KeysNotSet, // At least one existing key
}

impl fred::types::FromRedis for MsetnxReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::Integer(1) => Ok(Self::KeysSet),
            fred::types::RedisValue::Integer(0) => Ok(Self::KeysNotSet),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected MSETNX command reply",
            )),
        }
    }
}

#[derive(Debug)]
pub enum StreamCapKind {
    MinID,
    MaxLen,
}

impl From<StreamCapKind> for fred::types::XCapKind {
    fn from(item: StreamCapKind) -> Self {
        match item {
            StreamCapKind::MaxLen => Self::MaxLen,
            StreamCapKind::MinID => Self::MinID,
        }
    }
}

#[derive(Debug)]
pub enum StreamCapTrim {
    Exact,
    AlmostExact,
}

impl From<StreamCapTrim> for fred::types::XCapTrim {
    fn from(item: StreamCapTrim) -> Self {
        match item {
            StreamCapTrim::Exact => Self::Exact,
            StreamCapTrim::AlmostExact => Self::AlmostExact,
        }
    }
}

#[derive(Debug)]
pub enum DelReply {
    KeyDeleted,
    KeyNotDeleted, // Key not found
}

impl DelReply {
    pub fn is_key_deleted(&self) -> bool {
        matches!(self, Self::KeyDeleted)
    }

    pub fn is_key_not_deleted(&self) -> bool {
        matches!(self, Self::KeyNotDeleted)
    }
}

impl fred::types::FromRedis for DelReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::Integer(1) => Ok(Self::KeyDeleted),
            fred::types::RedisValue::Integer(0) => Ok(Self::KeyNotDeleted),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected del command reply",
            )),
        }
    }
}

#[derive(Debug)]
pub enum SaddReply {
    KeySet,
    KeyNotSet,
}

impl fred::types::FromRedis for SaddReply {
    fn from_value(value: fred::types::RedisValue) -> Result<Self, fred::error::RedisError> {
        match value {
            fred::types::RedisValue::Integer(1) => Ok(Self::KeySet),
            fred::types::RedisValue::Integer(0) => Ok(Self::KeyNotSet),
            _ => Err(fred::error::RedisError::new(
                fred::error::RedisErrorKind::Unknown,
                "Unexpected sadd command reply",
            )),
        }
    }
}

#[derive(Debug)]
pub enum SetGetReply<T> {
    ValueSet(T),    // Value was set and this is the value that was set
    ValueExists(T), // Value already existed and this is the existing value
}

impl<T> SetGetReply<T> {
    pub fn get_value(&self) -> &T {
        match self {
            Self::ValueSet(value) => value,
            Self::ValueExists(value) => value,
        }
    }
}

#[derive(Debug)]
pub struct RedisKey(String);

impl RedisKey {
    pub fn tenant_aware_key(&self, pool: &RedisConnectionPool) -> String {
        pool.add_prefix(&self.0)
    }

    pub fn tenant_unaware_key(&self, _pool: &RedisConnectionPool) -> String {
        self.0.clone()
    }
}

impl<T: AsRef<str>> From<T> for RedisKey {
    fn from(value: T) -> Self {
        let value = value.as_ref();

        Self(value.to_string())
    }
}


pub struct ProviderMetadata {
    provider_reference_id: Option<String>,
}


impl api::ConnectorAccessToken for Celero {}

impl api::ConnectorAccessToken for for Celero


impl Responder {
    let flow = Flow::PaymentsUpdate;
    let mut payload = json_payload.into_inner();
    if let Err(err) = payload
        .validate()
        .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
    {
        return api::log_and_return_error_response(err.into());
    };

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

    let payment_id = path.into_inner();

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

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

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

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


pub struct ChargebeeSubscriptionData {
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub current_term_start: Option<PrimitiveDateTime>,
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub next_billing_at: Option<PrimitiveDateTime>,
}


impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetDisputeMetricRequest");
        let flow = AnalyticsFlow::GetDisputeMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let profile_id = auth
                    .profile_id
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?;
                let auth: AuthInfo = AuthInfo::ProfileLevel {
                    org_id: org_id.clone(),
                    merchant_id: merchant_id.clone(),
                    profile_ids: vec![profile_id.clone()],
                };
                analytics::disputes::get_metrics(&state.pool, &auth, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub struct PayoutsResponseData {
    pub status: Option<common_enums::PayoutStatus>,
    pub connector_payout_id: Option<String>,
    pub payout_eligible: Option<bool>,
    pub should_add_next_step_to_process_tracker: bool,
    pub error_code: Option<String>,
    pub error_message: Option<String>,
}


pub struct DummyConnectorRefundRetrieveRequest {
    pub refund_id: String,
}


impl Responder {
    let flow = Flow::PaymentsCancelPostCapture;
    let mut payload = json_payload.into_inner();
    let payment_id = path.into_inner();

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

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


pub struct EliminationRoutingConfig {
    pub params: Option<Vec<DynamicRoutingConfigParams>>,
    pub elimination_analyser_config: Option<EliminationAnalyserConfig>,
    #[schema(value_type = DecisionEngineEliminationData)]
    pub decision_engine_configs: Option<open_router::DecisionEngineEliminationData>,
}


pub struct CustombillingRouterData<T> {
    pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}


pub struct DecryptedWalletDetails {
    pub card: WalletCardData,
    #[serde(rename = "cavv")]
    pub cryptogram: Secret<String>,
    #[serde(rename = "xid")]
    pub transaction_id: Secret<String>,
    pub wallet_type: FiservWalletType,
}


impl<T: DatabaseStore> PayoutsInterface for KVRouterStore<T> {}

impl PayoutsInterface for for KVRouterStore


pub async fn validate_request_and_initiate_payment_method_collect_link(
    _state: &SessionState,
    _merchant_context: &domain::MerchantContext,
    _req: &PaymentMethodCollectLinkRequest,
) -> RouterResult<PaymentMethodCollectLinkData> {
    todo!()
}


pub struct SquareSessionResponse {
    session_id: Secret<String>,
}


pub struct PaymentSuccessRate;


pub struct RefundResponse {
    id: String,
    status: RefundStatus,
}


pub async fn connector_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::ProfileId>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsList;
    let profile_id = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        profile_id.to_owned(),
        |state, auth::AuthenticationData { key_store, .. }, _, _| {
            list_connectors_for_a_profile(state, key_store, profile_id.clone())
        },
        auth::auth_type(
            &auth::AdminApiAuthWithMerchantIdFromHeader,
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantConnectorRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


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


Web Documentation: Smart Retries | Hyperswitch
# Type: Web Doc

Smart retry is a Hyperswitch feature to improve the payment success rates in a multi-processor setup. If the payment fails through the primary processor due to specific reasons, the payment will be retried with an alternative payment processor to increase the chances of making the payment successful.
There are two possible types of payment retry flows:
Smart Retries:
These retries are applicable where a user action is not required (after entering the card information) to complete a payment. Below are some example scenarios:
Scenario 1: If the payment is a non-3DS card transaction and the payment is declined by the primary processor due to technical or business failures, it will be retried.
Scenario 2: If the payment is a
3DS card transaction
and the payment is declined by the primary processor due to a
technical failure
, the payment
will be retried
.
Scenario 3: If the payment is a
3DS card transaction
and the payment is declined by the primary processor due to a
business failure
, the payment
will not be retried
.
User Consent-based Retries:
These retries are applicable for payment flows that need an additional level of user authentication (example: Apple Pay, Google Pay, 3DS cards, bank transfers).Such payment flows need an additional authentication from the user. Hence smart retries are not possible for such scenarios.
Note:
Currently, Hyperswitch supports Smart retries as an out-of-the-box capability. In order to enable user consent based retry for payment failures, you can create a fresh payment and re-trigger the Hyperswitch checkout.
How does it work?
Smart retry will be attempted whenever the payment fails through the Primary Processor for card transactions.
Primary processor is a first choice of payment processor for the particular transaction. This is evaluated based on the smart routing rules configured in the Hyperswitch dashboard’s routing module.
The flow looks like below.
How to enable Smart Retries?
Step 1:
Ensure that you have enabled the pecking order of payment processors on the Hyperswitch dashboard. You can access the settings from Routing > Default fallback > Manage.
Step 2:
Drop a request to
[email protected]
with the below information.
Confirmation to enable automatic retry
Maximum number of payment retry attempts (It is recommended to start with 1 retry attempt. However we can support more retry attempts based on the number of processors)
FAQs
What is a primary processor?
Primary processor is the first choice of processor for the particular transaction to be processed. This is evaluated based on the smart routing rules configured in the Hyperswitch dashboard’s routing module.
Why can I not enable Automatic Retry from the Hyperswitch dashboard?
For reconciliation purposes, some merchants prefer having the same payment_id being passed to both Hyperswitch and the Payment Processors. Smart retry would not be feasible if such a use case exists. Hence, Smart retry is as an additional configuration that can be enabled only by contacting our support (
[email protected]
).
Since Smart retry involves multiple payment attempts for a single payment_id, Hyperswitch appends the attempt number to the payment_id that the merchant sends to Hyperswitch before passing it on to the processors.
For example, if the merchant had sent pay_abcd145efg, then Hyperswitch will send the following payment_id to the processors during each attempt:
Payment attempt 1: pay_abcd145efg_1
Payment attempt 2: pay_abcd145efg_2
Payment attempt 3: pay_abcd145efg_3
What will the user experience look like during Smart Retry?
The user experience will not be different from a regular checkout experience, since all retry attempts will happen silently in the background. However, there is a possibility of the user receiving multiple payment attempt notifications / sms from the card issuing bank due to the card payment being attempted for more than once.
What is the difference between fallback and smart retry?
Fallback is a pecking order of all the configured processors which is used to route traffic standalone or when other smart routing rules are not applicable for the particular transaction. You can reorder the list with simple drag and drop from the Routing > Default fallback > Manage section in the dashboard.
Smart retry is a feature to improve the chances of success of a payment by silently retrying with an alternative processor.
19 days ago
Was this helpful?

Smart Retries | Hyperswitch


/// Validation for the SMTP server client specific configs
    pub fn validate(&self) -> Result<(), &'static str> {
        use common_utils::{ext_traits::ConfigExt, fp_utils::when};
        when(self.host.is_default_or_empty(), || {
            Err("email.smtp.host must not be empty")
        })?;
        self.username.clone().zip(self.password.clone()).map_or(
            Ok(()),
            |(username, password)| {
                when(username.peek().is_default_or_empty(), || {
                    Err("email.smtp.username must not be empty")
                })?;
                when(password.peek().is_default_or_empty(), || {
                    Err("email.smtp.password must not be empty")
                })
            },
        )?;
        Ok(())
    }


pub struct AuthorizedotnetRSyncResponse {
    transaction: Option<RSyncTransactionResponse>,
    messages: ResponseMessages,
}


OpenAPI Block Path: components.schemas.GooglePayThirdPartySdk
{
  "type": "object",
  "required": [
    "delayed_session_token",
    "connector",
    "sdk_next_action"
  ],
  "properties": {
    "delayed_session_token": {
      "type": "boolean",
      "description": "Identifier for the delayed session response"
    },
    "connector": {
      "type": "string",
      "description": "The name of the connector"
    },
    "sdk_next_action": {
      "$ref": "#/components/schemas/SdkNextAction"
    }
  }
}


pub struct StripebillingInvoiceData {
    pub object: StripebillingWebhookObject,
}


pub struct WorldpayPaymentsRequest {
    pub transaction_reference: String,
    pub merchant: Merchant,
    pub instruction: Instruction,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub customer: Option<Customer>,
}


impl Responder {
    let flow = Flow::AuthenticationAuthenticate;
    let authentication_id = path.into_inner();
    let api_auth = auth::ApiKeyAuth::default();
    let payload = AuthenticationAuthenticateRequest {
        authentication_id,
        ..json_payload.into_inner()
    };

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

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            unified_authentication_service::authentication_authenticate_core(
                state,
                merchant_context,
                req,
                auth_flow,
            )
        },
        &*auth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn connector_events_core(
    pool: &AnalyticsProvider,
    req: ConnectorEventsRequest,
    merchant_id: &common_utils::id_type::MerchantId,
) -> AnalyticsResult<Vec<ConnectorEventsResult>> {
    let data = match pool {
        AnalyticsProvider::Sqlx(_) => Err(FiltersError::NotImplemented(
            "Connector Events not implemented for SQLX",
        ))
        .attach_printable("SQL Analytics is not implemented for Connector Events"),
        AnalyticsProvider::Clickhouse(ckh_pool)
        | AnalyticsProvider::CombinedSqlx(_, ckh_pool)
        | AnalyticsProvider::CombinedCkh(_, ckh_pool) => {
            get_connector_events(merchant_id, req, ckh_pool).await
        }
    }
    .switch()?;
    Ok(data)
}


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


OpenAPI Block Path: components.schemas.PaymentMethodType
{
  "type": "string",
  "description": "Indicates the sub type of payment method. Eg: 'google_pay' & 'apple_pay' for wallets.",
  "enum": [
    "ach",
    "affirm",
    "afterpay_clearpay",
    "alfamart",
    "ali_pay",
    "ali_pay_hk",
    "alma",
    "amazon_pay",
    "paysera",
    "apple_pay",
    "atome",
    "bacs",
    "bancontact_card",
    "becs",
    "benefit",
    "bizum",
    "blik",
    "bluecode",
    "boleto",
    "bca_bank_transfer",
    "bni_va",
    "breadpay",
    "bri_va",
    "bhn_card_network",
    "card_redirect",
    "cimb_va",
    "classic",
    "credit",
    "crypto_currency",
    "cashapp",
    "dana",
    "danamon_va",
    "debit",
    "duit_now",
    "efecty",
    "eft",
    "eps",
    "flexiti",
    "fps",
    "evoucher",
    "giropay",
    "givex",
    "google_pay",
    "go_pay",
    "gcash",
    "ideal",
    "interac",
    "indomaret",
    "klarna",
    "kakao_pay",
    "local_bank_redirect",
    "mandiri_va",
    "knet",
    "mb_way",
    "mobile_pay",
    "momo",
    "momo_atm",
    "multibanco",
    "online_banking_thailand",
    "online_banking_czech_republic",
    "online_banking_finland",
    "online_banking_fpx",
    "online_banking_poland",
    "online_banking_slovakia",
    "oxxo",
    "pago_efectivo",
    "permata_bank_transfer",
    "open_banking_uk",
    "pay_bright",
    "paypal",
    "paze",
    "pix",
    "pay_safe_card",
    "przelewy24",
    "prompt_pay",
    "pse",
    "red_compra",
    "red_pagos",
    "samsung_pay",
    "sepa",
    "sepa_bank_transfer",
    "skrill",
    "sofort",
    "swish",
    "touch_n_go",
    "trustly",
    "twint",
    "upi_collect",
    "upi_intent",
    "vipps",
    "viet_qr",
    "venmo",
    "walley",
    "we_chat_pay",
    "seven_eleven",
    "lawson",
    "mini_stop",
    "family_mart",
    "seicomart",
    "pay_easy",
    "local_bank_transfer",
    "mifinity",
    "open_banking_pis",
    "direct_carrier_billing",
    "instant_bank_transfer",
    "instant_bank_transfer_finland",
    "instant_bank_transfer_poland",
    "revolut_pay",
    "indonesian_bank_transfer"
  ]
}


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

pub mod transformers;

use std::fmt::Debug;

use api_models::webhooks::{IncomingWebhookEvent, ObjectReferenceId};
use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::{BytesExt, ValueExt},
    request::{Method, Request, RequestBuilder, RequestContent},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    api::ApplicationResponse,
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsSessionRouterData, PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData,
        TokenizationRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks::{IncomingWebhook, IncomingWebhookFlowError, IncomingWebhookRequestDetails},
};
use lazy_static::lazy_static;
use masking::{ExposeInterface, Secret};
use transformers::{self as zsl, get_status};

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

#[derive(Debug, Clone)]
pub struct Zsl;

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

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

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

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

    fn common_get_content_type(&self) -> &'static str {
        "application/x-www-form-urlencoded"
    }

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

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response = serde_urlencoded::from_bytes::<zsl::ZslErrorResponse>(&res.response)
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        let error_reason = zsl::ZslResponseStatus::try_from(response.status.clone())?.to_string();

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.status,
            message: error_reason.clone(),
            reason: Some(error_reason),
            attempt_status: Some(common_enums::AttemptStatus::Failure),
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Zsl {
    fn is_webhook_source_verification_mandatory(&self) -> bool {
        true
    }
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Zsl {
    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> {
        Ok(format!("{}ecp", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_router_data = zsl::ZslRouterData::try_from((
            &self.get_currency_unit(),
            req.request.currency,
            req.request.amount,
            req,
        ))?;
        let connector_req = zsl::ZslPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::FormUrlEncoded(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 = serde_urlencoded::from_bytes::<zsl::ZslPaymentsResponse>(&res.response)
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i: &mut ConnectorEvent| 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)
    }

    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<PSync, PaymentsSyncData, PaymentsResponseData> for Zsl {
    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: zsl::ZslWebhookResponse = res
            .response
            .parse_struct("ZslWebhookResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i: &mut ConnectorEvent| 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,
        })
    }
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Zsl {
    fn build_request(
        &self,
        _req: &PaymentsSessionRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "Session flow".to_owned(),
            connector: "Zsl",
        }
        .into())
    }
}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Zsl
{
    fn build_request(
        &self,
        _req: &TokenizationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "PaymentMethod Tokenization flow ".to_owned(),
            connector: "Zsl",
        }
        .into())
    }
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Zsl {
    fn build_request(
        &self,
        _req: &RefreshTokenRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "AccessTokenAuth flow".to_owned(),
            connector: "Zsl",
        }
        .into())
    }
}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Zsl {
    fn build_request(
        &self,
        _req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "SetupMandate flow".to_owned(),
            connector: "Zsl",
        }
        .into())
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Zsl {
    fn build_request(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "Capture flow".to_owned(),
            connector: "Zsl",
        }
        .into())
    }
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Zsl {
    fn build_request(
        &self,
        _req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "Void flow ".to_owned(),
            connector: "Zsl",
        }
        .into())
    }
}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Zsl {
    fn build_request(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "Refund flow".to_owned(),
            connector: "Zsl",
        }
        .into())
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Zsl {
    fn build_request(
        &self,
        _req: &RefundSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "Rsync flow ".to_owned(),
            connector: "Zsl",
        }
        .into())
    }
}

#[async_trait::async_trait]
impl IncomingWebhook for Zsl {
    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, errors::ConnectorError> {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        Ok(ObjectReferenceId::PaymentId(
            api_models::payments::PaymentIdType::PaymentAttemptId(notif.mer_ref),
        ))
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;

        Ok(get_status(notif.status))
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let response = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
        Ok(Box::new(response))
    }

    async fn verify_webhook_source(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        connector_account_details: common_utils::crypto::Encryptable<Secret<serde_json::Value>>,
        _connector_label: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let connector_account_details = connector_account_details
            .parse_value::<ConnectorAuthType>("ConnectorAuthType")
            .change_context_lazy(|| errors::ConnectorError::WebhookSourceVerificationFailed)?;
        let auth_type = zsl::ZslAuthType::try_from(&connector_account_details)?;
        let key = auth_type.api_key.expose();
        let mer_id = auth_type.merchant_id.expose();
        let webhook_response = get_webhook_object_from_body(request.body)?;
        let signature = zsl::calculate_signature(
            webhook_response.enctype,
            zsl::ZslSignatureType::WebhookSignature {
                status: webhook_response.status,
                txn_id: webhook_response.txn_id,
                txn_date: webhook_response.txn_date,
                paid_ccy: webhook_response.paid_ccy.to_string(),
                paid_amt: webhook_response.paid_amt,
                mer_ref: webhook_response.mer_ref,
                mer_id,
                key,
            },
        )?;

        Ok(signature.eq(&webhook_response.signature))
    }

    fn get_webhook_api_response(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
        _error_kind: Option<IncomingWebhookFlowError>,
    ) -> CustomResult<ApplicationResponse<serde_json::Value>, errors::ConnectorError> {
        Ok(ApplicationResponse::TextPlain("CALLBACK-OK".to_string()))
    }
}

fn get_webhook_object_from_body(
    body: &[u8],
) -> CustomResult<zsl::ZslWebhookResponse, errors::ConnectorError> {
    let response: zsl::ZslWebhookResponse =
        serde_urlencoded::from_bytes::<zsl::ZslWebhookResponse>(body)
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
    Ok(response)
}

lazy_static! {
    static ref ZSL_SUPPORTED_PAYMENT_METHODS: SupportedPaymentMethods = {
        let supported_capture_methods = vec![enums::CaptureMethod::Automatic];

        let mut zsl_supported_payment_methods = SupportedPaymentMethods::new();
        zsl_supported_payment_methods.add(
            enums::PaymentMethod::BankTransfer,
            enums::PaymentMethodType::LocalBankTransfer,
            PaymentMethodDetails{
                mandates: common_enums::FeatureStatus::NotSupported,
                refunds: common_enums::FeatureStatus::NotSupported,
                supported_capture_methods,
                specific_features: None,
            },
        );

        zsl_supported_payment_methods
    };

    static ref ZSL_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "ZSL",
        description:
            "Zsl is a payment gateway operating in China, specializing in facilitating local bank transfers",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Live,
    };

    static ref ZSL_SUPPORTED_WEBHOOK_FLOWS: Vec<enums::EventClass> = Vec::new();

}

impl ConnectorSpecifications for Zsl {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&*ZSL_CONNECTOR_INFO)
    }

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

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


pub struct Score {
    factor_codes: Option<Vec<String>>,
    result: Option<RiskResult>,
    model_used: Option<String>,
}


OpenAPI Block Path: components.schemas.NextActionCall
{
  "type": "string",
  "enum": [
    "post_session_tokens",
    "confirm",
    "sync",
    "complete_authorize"
  ]
}


impl MandateInterface for KafkaStore {
    async fn find_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Mandate, errors::StorageError> {
        self.diesel_store
            .find_mandate_by_merchant_id_mandate_id(merchant_id, mandate_id, storage_scheme)
            .await
    }

    async fn find_mandate_by_merchant_id_connector_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_mandate_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Mandate, errors::StorageError> {
        self.diesel_store
            .find_mandate_by_merchant_id_connector_mandate_id(
                merchant_id,
                connector_mandate_id,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn find_mandate_by_global_customer_id(
        &self,
        id: &id_type::GlobalCustomerId,
    ) -> CustomResult<Vec<storage::Mandate>, errors::StorageError> {
        self.diesel_store
            .find_mandate_by_global_customer_id(id)
            .await
    }

    async fn find_mandate_by_merchant_id_customer_id(
        &self,
        merchant_id: &id_type::MerchantId,
        customer_id: &id_type::CustomerId,
    ) -> CustomResult<Vec<storage::Mandate>, errors::StorageError> {
        self.diesel_store
            .find_mandate_by_merchant_id_customer_id(merchant_id, customer_id)
            .await
    }

    async fn update_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        mandate_update: storage::MandateUpdate,
        mandate: storage::Mandate,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Mandate, errors::StorageError> {
        self.diesel_store
            .update_mandate_by_merchant_id_mandate_id(
                merchant_id,
                mandate_id,
                mandate_update,
                mandate,
                storage_scheme,
            )
            .await
    }

    async fn find_mandates_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_constraints: api_models::mandates::MandateListConstraints,
    ) -> CustomResult<Vec<storage::Mandate>, errors::StorageError> {
        self.diesel_store
            .find_mandates_by_merchant_id(merchant_id, mandate_constraints)
            .await
    }

    async fn insert_mandate(
        &self,
        mandate: storage::MandateNew,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Mandate, errors::StorageError> {
        self.diesel_store
            .insert_mandate(mandate, storage_scheme)
            .await
    }
}

impl MandateInterface for for KafkaStore


pub struct FiservSessionObject {
    pub terminal_id: Secret<String>,
}


pub struct ArchipelRefundRequest {
    pub order: ArchipelRefundOrder,
    pub tenant_id: ArchipelTenantId,
}


pub struct AuthoriseIntegrityObject {
    /// Authorise amount
    pub amount: MinorUnit,
    /// Authorise currency
    pub currency: storage_enums::Currency,
}


File: crates/hyperswitch_connectors/src/connectors/taxjar/transformers.rs
 Public structs: 8

use common_enums::enums;
use common_utils::types::{FloatMajorUnit, FloatMajorUnitForConnector};
use error_stack::report;
use hyperswitch_domain_models::{
    router_data::{ConnectorAuthType, RouterData},
    router_request_types::PaymentsTaxCalculationData,
    router_response_types::TaxCalculationResponseData,
    types,
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::ResponseRouterData,
    utils::{self, AddressDetailsData},
};

pub struct TaxjarRouterData<T> {
    pub amount: FloatMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub order_amount: FloatMajorUnit,
    pub shipping: FloatMajorUnit,
    pub router_data: T,
}

impl<T> From<(FloatMajorUnit, FloatMajorUnit, FloatMajorUnit, T)> for TaxjarRouterData<T> {
    fn from(
        (amount, order_amount, shipping, item): (FloatMajorUnit, FloatMajorUnit, FloatMajorUnit, T),
    ) -> Self {
        Self {
            amount,
            order_amount,
            shipping,
            router_data: item,
        }
    }
}

#[derive(Default, Debug, Serialize, PartialEq)]
pub struct TaxjarPaymentsRequest {
    to_country: enums::CountryAlpha2,
    to_zip: Secret<String>,
    to_state: Secret<String>,
    to_city: Option<String>,
    to_street: Option<Secret<String>>,
    amount: FloatMajorUnit,
    shipping: FloatMajorUnit,
    line_items: Vec<LineItem>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct LineItem {
    id: Option<String>,
    quantity: Option<u16>,
    product_tax_code: Option<String>,
    unit_price: Option<FloatMajorUnit>,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct TaxjarCard {
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
    complete: bool,
}

impl TryFrom<&TaxjarRouterData<&types::PaymentsTaxCalculationRouterData>>
    for TaxjarPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &TaxjarRouterData<&types::PaymentsTaxCalculationRouterData>,
    ) -> Result<Self, Self::Error> {
        let request = &item.router_data.request;
        let shipping = &item
            .router_data
            .request
            .shipping_address
            .address
            .clone()
            .ok_or(errors::ConnectorError::MissingRequiredField {
                field_name: "address",
            })?;

        match request.order_details.clone() {
            Some(order_details) => {
                let line_items: Result<Vec<LineItem>, error_stack::Report<errors::ConnectorError>> =
                    order_details
                        .iter()
                        .map(|line_item| {
                            Ok(LineItem {
                                id: line_item.product_id.clone(),
                                quantity: Some(line_item.quantity),
                                product_tax_code: line_item.product_tax_code.clone(),
                                unit_price: Some(item.order_amount),
                            })
                        })
                        .collect();

                Ok(Self {
                    to_country: shipping.get_country()?.to_owned(),
                    to_zip: shipping.get_zip()?.to_owned(),
                    to_state: shipping.to_state_code()?.to_owned(),
                    to_city: shipping.get_optional_city(),
                    to_street: shipping.get_optional_line1(),
                    amount: item.amount,
                    shipping: item.shipping,
                    line_items: line_items?,
                })
            }
            None => Err(report!(errors::ConnectorError::MissingRequiredField {
                field_name: "order_details"
            })),
        }
    }
}

pub struct TaxjarAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for TaxjarAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                api_key: api_key.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TaxjarPaymentsResponse {
    tax: Tax,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Tax {
    amount_to_collect: FloatMajorUnit, //calculated_tax_amount
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            TaxjarPaymentsResponse,
            PaymentsTaxCalculationData,
            TaxCalculationResponseData,
        >,
    > for RouterData<F, PaymentsTaxCalculationData, TaxCalculationResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            TaxjarPaymentsResponse,
            PaymentsTaxCalculationData,
            TaxCalculationResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let currency = item.data.request.currency;
        let amount_to_collect = item.response.tax.amount_to_collect;
        let calculated_tax = utils::convert_back_amount_to_minor_units(
            &FloatMajorUnitForConnector,
            amount_to_collect,
            currency,
        )?;

        Ok(Self {
            response: Ok(TaxCalculationResponseData {
                order_tax_amount: calculated_tax,
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct TaxjarErrorResponse {
    pub status: i64,
    pub error: String,
    pub detail: String,
}


pub struct ApplePayCryptogramDataInternal {
    pub online_payment_cryptogram: Secret<String>,
    pub eci_indicator: Option<String>,
}


impl PaymentToken for Threedsecureio {}

impl PaymentToken for for Threedsecureio


OpenAPI Block Path: components.schemas.OpenBankingSessionToken
{
  "type": "object",
  "required": [
    "open_banking_session_token"
  ],
  "properties": {
    "open_banking_session_token": {
      "type": "string",
      "description": "The session token for OpenBanking Connectors"
    }
  }
}


pub struct Webhooks;