                &(state.into()),
                merchant_context.get_merchant_key_store(),
                payment_method,
                pm_update,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to update payment method in db")?;

            Err(e)
        }
    }?;

    Ok((response, payment_method))
}

// network tokenization and vaulting to locker is not required for proxy card since the card is already tokenized
#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn create_payment_method_proxy_card_core(
    state: &SessionState,
    req: api::PaymentMethodCreate,
    merchant_context: &domain::MerchantContext,
    profile: &domain::Profile,
    merchant_id: &id_type::MerchantId,
    customer_id: &id_type::GlobalCustomerId,
    payment_method_id: id_type::GlobalPaymentMethodId,
    payment_method_billing_address: Option<
        Encryptable<hyperswitch_domain_models::address::Address>,
    >,
) -> RouterResult<(api::PaymentMethodResponse, domain::PaymentMethod)> {
    use crate::core::payment_methods::vault::Vault;

    let key_manager_state = &(state).into();

    let external_vault_source = profile
        .external_vault_connector_details
        .clone()
        .map(|details| details.vault_connector_id);

    let additional_payment_method_data = Some(
        req.payment_method_data
            .populate_bin_details_for_payment_method(state)
            .await
            .convert_to_additional_payment_method_data()?,
    );

    let encrypted_payment_method_data = additional_payment_method_data
        .async_map(|payment_method_data| {
            cards::create_encrypted_data(
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                payment_method_data,
            )
        })
        .await
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to encrypt Payment method data")?
        .map(|encoded_pmd| {
            encoded_pmd.deserialize_inner_value(|value| value.parse_value("PaymentMethodsData"))
        })
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to parse Payment method data")?;

    let external_vault_token_data = req.payment_method_data.get_external_vault_token_data();

    let encrypted_external_vault_token_data = external_vault_token_data
        .async_map(|external_vault_token_data| {
            cards::create_encrypted_data(
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                external_vault_token_data,
            )
        })
        .await
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to encrypt External vault token data")?
        .map(|encoded_data| {
            encoded_data
                .deserialize_inner_value(|value| value.parse_value("ExternalVaultTokenData"))
        })
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to parse External vault token data")?;

    let vault_type = external_vault_source
        .is_some()
        .then_some(common_enums::VaultType::External);

    let payment_method = create_payment_method_for_confirm(
        state,
        customer_id,
        payment_method_id,
        external_vault_source,
        merchant_id,
        merchant_context.get_merchant_key_store(),
        merchant_context.get_merchant_account().storage_scheme,
        req.payment_method_type,
        req.payment_method_subtype,
        payment_method_billing_address,
        encrypted_payment_method_data,
        encrypted_external_vault_token_data,
        vault_type,
    )
    .await?;

    let payment_method_response =
        pm_transforms::generate_payment_method_response(&payment_method, &None)?;

    Ok((payment_method_response, payment_method))
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug)]
pub struct NetworkTokenPaymentMethodDetails {
    network_token_requestor_reference_id: String,
    network_token_locker_id: String,
    network_token_pmd: Encryptable<Secret<serde_json::Value>>,
}

#[cfg(feature = "v2")]
pub async fn network_tokenize_and_vault_the_pmd(
    state: &SessionState,
    payment_method_data: &domain::PaymentMethodVaultingData,
    merchant_context: &domain::MerchantContext,
    network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,
    network_tokenization_enabled_for_profile: bool,
    customer_id: &id_type::GlobalCustomerId,
) -> Option<NetworkTokenPaymentMethodDetails> {
    let network_token_pm_details_result: CustomResult<
        NetworkTokenPaymentMethodDetails,
        errors::NetworkTokenizationError,
    > = async {
        when(!network_tokenization_enabled_for_profile, || {
            Err(report!(
                errors::NetworkTokenizationError::NetworkTokenizationNotEnabledForProfile
            ))
        })?;

        let is_network_tokenization_enabled_for_pm = network_tokenization
            .as_ref()
            .map(|nt| matches!(nt.enable, common_enums::NetworkTokenizationToggle::Enable))
            .unwrap_or(false);

        let card_data = payment_method_data
            .get_card()
            .and_then(|card| is_network_tokenization_enabled_for_pm.then_some(card))
            .ok_or_else(|| {
                report!(errors::NetworkTokenizationError::NotSupported {
                    message: "Payment method".to_string(),
                })
            })?;

        let (resp, network_token_req_ref_id) =
            network_tokenization::make_card_network_tokenization_request(
                state,
                card_data,
                customer_id,
            )
            .await?;

        let network_token_vaulting_data = domain::PaymentMethodVaultingData::NetworkToken(resp);
        let vaulting_resp = vault::add_payment_method_to_vault(
            state,
            merchant_context,
            &network_token_vaulting_data,
            None,
            customer_id,
        )
        .await
        .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed)
        .attach_printable("Failed to vault network token")?;

        let key_manager_state = &(state).into();
        let network_token_pmd = cards::create_encrypted_data(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            network_token_vaulting_data.get_payment_methods_data(),
        )
        .await
        .change_context(errors::NetworkTokenizationError::NetworkTokenDetailsEncryptionFailed)
        .attach_printable("Failed to encrypt PaymentMethodsData")?;

        Ok(NetworkTokenPaymentMethodDetails {
            network_token_requestor_reference_id: network_token_req_ref_id,
            network_token_locker_id: vaulting_resp.vault_id.get_string_repr().clone(),
            network_token_pmd,
        })
    }
    .await;
    network_token_pm_details_result.ok()
}

#[cfg(feature = "v2")]
pub async fn populate_bin_details_for_payment_method(
    state: &SessionState,
    payment_method_data: &domain::PaymentMethodVaultingData,
) -> domain::PaymentMethodVaultingData {
    match payment_method_data {
        domain::PaymentMethodVaultingData::Card(card) => {
            let card_isin = card.card_number.get_card_isin();

            if card.card_issuer.is_some()
                && card.card_network.is_some()
                && card.card_type.is_some()
                && card.card_issuing_country.is_some()
            {
                domain::PaymentMethodVaultingData::Card(card.clone())
            } else {
                let card_info = state
                    .store
                    .get_card_info(&card_isin)
                    .await
                    .map_err(|error| services::logger::error!(card_info_error=?error))
                    .ok()
                    .flatten();

                domain::PaymentMethodVaultingData::Card(payment_methods::CardDetail {
                    card_number: card.card_number.clone(),
                    card_exp_month: card.card_exp_month.clone(),
                    card_exp_year: card.card_exp_year.clone(),
                    card_holder_name: card.card_holder_name.clone(),
                    nick_name: card.nick_name.clone(),
                    card_issuing_country: card_info.as_ref().and_then(|val| {
                        val.card_issuing_country
                            .as_ref()
                            .map(|c| api_enums::CountryAlpha2::from_str(c))
                            .transpose()
                            .ok()
                            .flatten()
                    }),
                    card_network: card_info.as_ref().and_then(|val| val.card_network.clone()),
                    card_issuer: card_info.as_ref().and_then(|val| val.card_issuer.clone()),
                    card_type: card_info.as_ref().and_then(|val| {
                        val.card_type
                            .as_ref()
                            .map(|c| payment_methods::CardType::from_str(c))
                            .transpose()
                            .ok()
                            .flatten()
                    }),
                    card_cvc: card.card_cvc.clone(),
                })
            }
        }
        _ => payment_method_data.clone(),
    }
}
#[cfg(feature = "v2")]
#[async_trait::async_trait]
pub trait PaymentMethodExt {
    async fn populate_bin_details_for_payment_method(&self, state: &SessionState) -> Self;

    // convert to data format compatible to save in payment method table
    fn convert_to_additional_payment_method_data(
        &self,
    ) -> RouterResult<payment_methods::PaymentMethodsData>;

    // get tokens generated from external vault
    fn get_external_vault_token_data(&self) -> Option<payment_methods::ExternalVaultTokenData>;
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl PaymentMethodExt for domain::PaymentMethodVaultingData {
    async fn populate_bin_details_for_payment_method(&self, state: &SessionState) -> Self {
        match self {
            Self::Card(card) => {
                let card_isin = card.card_number.get_card_isin();

                if card.card_issuer.is_some()
                    && card.card_network.is_some()
                    && card.card_type.is_some()
                    && card.card_issuing_country.is_some()
                {
                    Self::Card(card.clone())
                } else {
                    let card_info = state
                        .store
                        .get_card_info(&card_isin)
                        .await
                        .map_err(|error| services::logger::error!(card_info_error=?error))
                        .ok()
                        .flatten();

                    Self::Card(payment_methods::CardDetail {
                        card_number: card.card_number.clone(),
                        card_exp_month: card.card_exp_month.clone(),
                        card_exp_year: card.card_exp_year.clone(),
                        card_holder_name: card.card_holder_name.clone(),
                        nick_name: card.nick_name.clone(),
                        card_issuing_country: card_info.as_ref().and_then(|val| {
                            val.card_issuing_country
                                .as_ref()
                                .map(|c| api_enums::CountryAlpha2::from_str(c))
                                .transpose()
                                .ok()
                                .flatten()
                        }),
                        card_network: card_info.as_ref().and_then(|val| val.card_network.clone()),
                        card_issuer: card_info.as_ref().and_then(|val| val.card_issuer.clone()),
                        card_type: card_info.as_ref().and_then(|val| {
                            val.card_type
                                .as_ref()
                                .map(|c| payment_methods::CardType::from_str(c))
                                .transpose()
                                .ok()
                                .flatten()
                        }),
                        card_cvc: card.card_cvc.clone(),
                    })
                }
            }
            _ => self.clone(),
        }
    }

    // Not implement because it is saved in locker and not in payment method table
    fn convert_to_additional_payment_method_data(
        &self,
    ) -> RouterResult<payment_methods::PaymentMethodsData> {
        Err(report!(errors::ApiErrorResponse::UnprocessableEntity {
            message: "Payment method data is not supported".to_string()
        })
        .attach_printable("Payment method data is not supported"))
    }

    fn get_external_vault_token_data(&self) -> Option<payment_methods::ExternalVaultTokenData> {
        None
    }
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl PaymentMethodExt for payment_methods::PaymentMethodCreateData {
    async fn populate_bin_details_for_payment_method(&self, state: &SessionState) -> Self {
        match self {
            Self::ProxyCard(card) => {
                let card_isin = card.bin_number.clone();

                if card.card_issuer.is_some()
                    && card.card_network.is_some()
                    && card.card_type.is_some()
                    && card.card_issuing_country.is_some()
                {
                    Self::ProxyCard(card.clone())
                } else if let Some(card_isin) = card_isin {
                    let card_info = state
                        .store
                        .get_card_info(&card_isin)
                        .await
                        .map_err(|error| services::logger::error!(card_info_error=?error))
                        .ok()
                        .flatten();

                    Self::ProxyCard(payment_methods::ProxyCardDetails {
                        card_number: card.card_number.clone(),
                        card_exp_month: card.card_exp_month.clone(),
                        card_exp_year: card.card_exp_year.clone(),
                        card_holder_name: card.card_holder_name.clone(),
                        bin_number: card.bin_number.clone(),
                        last_four: card.last_four.clone(),
                        nick_name: card.nick_name.clone(),
                        card_issuing_country: card_info
                            .as_ref()
                            .and_then(|val| val.card_issuing_country.clone()),
                        card_network: card_info.as_ref().and_then(|val| val.card_network.clone()),
                        card_issuer: card_info.as_ref().and_then(|val| val.card_issuer.clone()),
                        card_type: card_info.as_ref().and_then(|val| val.card_type.clone()),
                        card_cvc: card.card_cvc.clone(),
                    })
                } else {
                    Self::ProxyCard(card.clone())
                }
            }
            _ => self.clone(),
        }
    }

    fn convert_to_additional_payment_method_data(
        &self,
    ) -> RouterResult<payment_methods::PaymentMethodsData> {
        match self.clone() {
            Self::ProxyCard(card_details) => Ok(payment_methods::PaymentMethodsData::Card(
                payment_methods::CardDetailsPaymentMethod {
                    last4_digits: card_details.last_four,
                    expiry_month: Some(card_details.card_exp_month),
                    expiry_year: Some(card_details.card_exp_year),
                    card_holder_name: card_details.card_holder_name,
                    nick_name: card_details.nick_name,
                    issuer_country: card_details.card_issuing_country,
                    card_network: card_details.card_network,
                    card_issuer: card_details.card_issuer,
                    card_type: card_details.card_type,
                    card_isin: card_details.bin_number,
                    saved_to_locker: false,
                    co_badged_card_data: None,
                },
            )),
            Self::Card(card_details) => Ok(payment_methods::PaymentMethodsData::Card(
                payment_methods::CardDetailsPaymentMethod {
                    expiry_month: Some(card_details.card_exp_month),
                    expiry_year: Some(card_details.card_exp_year),
                    card_holder_name: card_details.card_holder_name,
                    nick_name: card_details.nick_name,
                    issuer_country: card_details
                        .card_issuing_country
                        .map(|country| country.to_string()),
                    card_network: card_details.card_network,
                    card_issuer: card_details.card_issuer,
                    card_type: card_details
                        .card_type
                        .map(|card_type| card_type.to_string()),
                    saved_to_locker: false,
                    card_isin: None,
                    last4_digits: None,
                    co_badged_card_data: None,
                },
            )),
        }
    }

    fn get_external_vault_token_data(&self) -> Option<payment_methods::ExternalVaultTokenData> {
        match self.clone() {
            Self::ProxyCard(card_details) => Some(payment_methods::ExternalVaultTokenData {
                tokenized_card_number: card_details.card_number,
            }),
            Self::Card(_) => None,
        }
    }
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn payment_method_intent_create(
    state: &SessionState,
    req: api::PaymentMethodIntentCreate,
    merchant_context: &domain::MerchantContext,
) -> RouterResponse<api::PaymentMethodResponse> {
    let db = &*state.store;
    let merchant_id = merchant_context.get_merchant_account().get_id();
    let customer_id = req.customer_id.to_owned();
    let key_manager_state = &(state).into();

    db.find_customer_by_global_id(
        key_manager_state,
        &customer_id,
        merchant_context.get_merchant_key_store(),
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)
    .attach_printable("Customer not found for the payment method")?;

    let payment_method_billing_address = req
        .billing
        .clone()
        .async_map(|billing| {
            cards::create_encrypted_data(
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                billing,
            )
        })
        .await
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to encrypt Payment method billing address")?
        .map(|encoded_address| {
            encoded_address.deserialize_inner_value(|value| value.parse_value("Address"))
        })
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to parse Payment method billing address")?;

    // create pm entry

    let payment_method_id =
        id_type::GlobalPaymentMethodId::generate(&state.conf.cell_information.id)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to generate GlobalPaymentMethodId")?;

    let payment_method = create_payment_method_for_intent(
        state,
        req.metadata.clone(),
        &customer_id,
        payment_method_id,
        merchant_id,
        merchant_context.get_merchant_key_store(),
        merchant_context.get_merchant_account().storage_scheme,
        payment_method_billing_address,
    )
    .await
    .attach_printable("Failed to add Payment method to DB")?;

    let resp = pm_transforms::generate_payment_method_response(&payment_method, &None)?;

    Ok(services::ApplicationResponse::Json(resp))
}

#[cfg(feature = "v2")]
trait PerformFilteringOnEnabledPaymentMethods {
    fn perform_filtering(self) -> FilteredPaymentMethodsEnabled;
}

#[cfg(feature = "v2")]
impl PerformFilteringOnEnabledPaymentMethods
    for hyperswitch_domain_models::merchant_connector_account::FlattenedPaymentMethodsEnabled
{
    fn perform_filtering(self) -> FilteredPaymentMethodsEnabled {
        FilteredPaymentMethodsEnabled(self.payment_methods_enabled)
    }
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn list_payment_methods_for_session(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
) -> RouterResponse<api::PaymentMethodListResponseForSession> {
    let key_manager_state = &(&state).into();

    let db = &*state.store;

    let payment_method_session = db
        .get_payment_methods_session(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &payment_method_session_id,
        )
        .await
        .change_context(errors::ApiErrorResponse::PaymentMethodNotFound)
        .attach_printable("Unable to find payment method")?;

    let payment_connector_accounts = db
        .list_enabled_connector_accounts_by_profile_id(
            key_manager_state,
            profile.get_id(),
            merchant_context.get_merchant_key_store(),
            common_enums::ConnectorType::PaymentProcessor,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("error when fetching merchant connector accounts")?;

    let customer_payment_methods = list_customer_payment_methods_core(
        &state,
        &merchant_context,
        &payment_method_session.customer_id,
    )
    .await?;

    let response =
        hyperswitch_domain_models::merchant_connector_account::FlattenedPaymentMethodsEnabled::from_payment_connectors_list(payment_connector_accounts)
            .perform_filtering()
            .get_required_fields(RequiredFieldsInput::new(state.conf.required_fields.clone()))
            .generate_response_for_session(customer_payment_methods);

    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        response,
    ))
}

#[cfg(all(feature = "v2", feature = "olap"))]
#[instrument(skip_all)]
pub async fn list_saved_payment_methods_for_customer(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    customer_id: id_type::GlobalCustomerId,
) -> RouterResponse<payment_methods::CustomerPaymentMethodsListResponse> {
    let customer_payment_methods =
        list_payment_methods_core(&state, &merchant_context, &customer_id).await?;

    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        customer_payment_methods,
    ))
}

#[cfg(all(feature = "v2", feature = "olap"))]
#[instrument(skip_all)]
pub async fn get_token_data_for_payment_method(
    state: SessionState,
    merchant_account: domain::MerchantAccount,
    key_store: domain::MerchantKeyStore,
    profile: domain::Profile,
    request: payment_methods::GetTokenDataRequest,
    payment_method_id: id_type::GlobalPaymentMethodId,
) -> RouterResponse<api::TokenDataResponse> {
    let key_manager_state = &(&state).into();

    let db = &*state.store;

    let payment_method = db
        .find_payment_method(
            key_manager_state,
            &key_store,
            &payment_method_id,
            merchant_account.storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

    let token_data_response =
        generate_token_data_response(&state, request, profile, &payment_method).await?;

    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        token_data_response,
    ))
}

#[cfg(all(feature = "v2", feature = "olap"))]
#[instrument(skip_all)]
pub async fn generate_token_data_response(
    state: &SessionState,
    request: payment_methods::GetTokenDataRequest,
    profile: domain::Profile,
    payment_method: &domain::PaymentMethod,
) -> RouterResult<api::TokenDataResponse> {
    let token_details = match request.token_type {
        common_enums::TokenDataType::NetworkToken => {
            let is_network_tokenization_enabled = profile.is_network_tokenization_enabled;
            if !is_network_tokenization_enabled {
                return Err(errors::ApiErrorResponse::UnprocessableEntity {
                    message: "Network tokenization is not enabled for this profile".to_string(),
                }
                .into());
            }
            let network_token_requestor_ref_id = payment_method
                .network_token_requestor_reference_id
                .clone()
                .ok_or(errors::ApiErrorResponse::GenericNotFoundError {
                    message: "NetworkTokenRequestorReferenceId is not present".to_string(),
                })?;

            let network_token = network_tokenization::get_token_from_tokenization_service(
                state,
                network_token_requestor_ref_id,
                payment_method,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("failed to fetch network token data from tokenization service")?;

            api::TokenDetailsResponse::NetworkTokenDetails(api::NetworkTokenDetailsResponse {
                network_token: network_token.network_token,
                network_token_exp_month: network_token.network_token_exp_month,
                network_token_exp_year: network_token.network_token_exp_year,
                cryptogram: network_token.cryptogram,
                card_issuer: network_token.card_issuer,
                card_network: network_token.card_network,
                card_type: network_token.card_type,
                card_issuing_country: network_token.card_issuing_country,
                bank_code: network_token.bank_code,
                card_holder_name: network_token.card_holder_name,
                nick_name: network_token.nick_name,
                eci: network_token.eci,
            })
        }
        common_enums::TokenDataType::SingleUseToken
        | common_enums::TokenDataType::MultiUseToken => {
            return Err(errors::ApiErrorResponse::UnprocessableEntity {
                message: "Token type not supported".to_string(),
            }
            .into());
        }
    };

    Ok(api::TokenDataResponse {
        payment_method_id: payment_method.id.clone(),
        token_type: request.token_type,
        token_details,
    })
}

#[cfg(all(feature = "v2", feature = "olap"))]
#[instrument(skip_all)]
pub async fn get_total_saved_payment_methods_for_merchant(
    state: SessionState,
    merchant_context: domain::MerchantContext,
) -> RouterResponse<api::TotalPaymentMethodCountResponse> {
    let total_payment_method_count =
        get_total_payment_method_count_core(&state, &merchant_context).await?;

    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        total_payment_method_count,
    ))
}

#[cfg(feature = "v2")]
/// Container for the inputs required for the required fields
struct RequiredFieldsInput {
    required_fields_config: settings::RequiredFields,
}

#[cfg(feature = "v2")]
impl RequiredFieldsInput {
    fn new(required_fields_config: settings::RequiredFields) -> Self {
        Self {
            required_fields_config,
        }
    }
}

#[cfg(feature = "v2")]
/// Container for the filtered payment methods
struct FilteredPaymentMethodsEnabled(
    Vec<hyperswitch_domain_models::merchant_connector_account::PaymentMethodsEnabledForConnector>,
);

#[cfg(feature = "v2")]
trait GetRequiredFields {
    fn get_required_fields(
        &self,
        payment_method_enabled: &hyperswitch_domain_models::merchant_connector_account::PaymentMethodsEnabledForConnector,
    ) -> Option<&settings::RequiredFieldFinal>;
}

#[cfg(feature = "v2")]
impl GetRequiredFields for settings::RequiredFields {
    fn get_required_fields(
        &self,
        payment_method_enabled: &hyperswitch_domain_models::merchant_connector_account::PaymentMethodsEnabledForConnector,
    ) -> Option<&settings::RequiredFieldFinal> {
        self.0
            .get(&payment_method_enabled.payment_method)
            .and_then(|required_fields_for_payment_method| {
                required_fields_for_payment_method.0.get(
                    &payment_method_enabled
                        .payment_methods_enabled
                        .payment_method_subtype,
                )
            })
            .map(|connector_fields| &connector_fields.fields)
            .and_then(|connector_hashmap| connector_hashmap.get(&payment_method_enabled.connector))
    }
}

#[cfg(feature = "v2")]
impl FilteredPaymentMethodsEnabled {
    fn get_required_fields(
        self,
        input: RequiredFieldsInput,
    ) -> RequiredFieldsForEnabledPaymentMethodTypes {
        let required_fields_config = input.required_fields_config;

        let required_fields_info = self
            .0
            .into_iter()
            .map(|payment_methods_enabled| {
                let required_fields =
                    required_fields_config.get_required_fields(&payment_methods_enabled);

                let required_fields = required_fields
                    .map(|required_fields| {
                        let common_required_fields = required_fields
                            .common
                            .iter()
                            .flatten()
                            .map(ToOwned::to_owned);

                        // Collect mandate required fields because this is for zero auth mandates only
                        let mandate_required_fields = required_fields
                            .mandate
                            .iter()
                            .flatten()
                            .map(ToOwned::to_owned);

                        // Combine both common and mandate required fields
                        common_required_fields
                            .chain(mandate_required_fields)
                            .collect::<Vec<_>>()
                    })
                    .unwrap_or_default();

                RequiredFieldsForEnabledPaymentMethod {
                    required_fields,
                    payment_method_type: payment_methods_enabled.payment_method,
                    payment_method_subtype: payment_methods_enabled
                        .payment_methods_enabled
                        .payment_method_subtype,
                }
            })
            .collect();

        RequiredFieldsForEnabledPaymentMethodTypes(required_fields_info)
    }
}

#[cfg(feature = "v2")]
/// Element container to hold the filtered payment methods with required fields
struct RequiredFieldsForEnabledPaymentMethod {
    required_fields: Vec<payment_methods::RequiredFieldInfo>,
    payment_method_subtype: common_enums::PaymentMethodType,
    payment_method_type: common_enums::PaymentMethod,
}

#[cfg(feature = "v2")]
/// Container to hold the filtered payment methods enabled with required fields
struct RequiredFieldsForEnabledPaymentMethodTypes(Vec<RequiredFieldsForEnabledPaymentMethod>);

#[cfg(feature = "v2")]
impl RequiredFieldsForEnabledPaymentMethodTypes {
    fn generate_response_for_session(
        self,
        customer_payment_methods: Vec<payment_methods::CustomerPaymentMethodResponseItem>,
    ) -> payment_methods::PaymentMethodListResponseForSession {
        let response_payment_methods = self
            .0
            .into_iter()
            .map(
                |payment_methods_enabled| payment_methods::ResponsePaymentMethodTypes {
                    payment_method_type: payment_methods_enabled.payment_method_type,
                    payment_method_subtype: payment_methods_enabled.payment_method_subtype,
                    required_fields: payment_methods_enabled.required_fields,
                    extra_information: None,
                },
            )
            .collect();

        payment_methods::PaymentMethodListResponseForSession {
            payment_methods_enabled: response_payment_methods,
            customer_payment_methods,
        }
    }
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn create_payment_method_for_intent(
    state: &SessionState,
    metadata: Option<common_utils::pii::SecretSerdeValue>,
    customer_id: &id_type::GlobalCustomerId,
    payment_method_id: id_type::GlobalPaymentMethodId,
    merchant_id: &id_type::MerchantId,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: enums::MerchantStorageScheme,
    payment_method_billing_address: Option<
        Encryptable<hyperswitch_domain_models::address::Address>,
    >,
) -> CustomResult<domain::PaymentMethod, errors::ApiErrorResponse> {
    use josekit::jwe::zip::deflate::DeflateJweCompression::Def;

    let db = &*state.store;

    let current_time = common_utils::date_time::now();

    let response = db
        .insert_payment_method(
            &state.into(),
            key_store,
            domain::PaymentMethod {
                customer_id: customer_id.to_owned(),
                merchant_id: merchant_id.to_owned(),
                id: payment_method_id,
                locker_id: None,
                payment_method_type: None,
                payment_method_subtype: None,
                payment_method_data: None,
                connector_mandate_details: None,
                customer_acceptance: None,
                client_secret: None,
                status: enums::PaymentMethodStatus::AwaitingData,
                network_transaction_id: None,
                created_at: current_time,
                last_modified: current_time,
                last_used_at: current_time,
                payment_method_billing_address,
                updated_by: None,
                version: common_types::consts::API_VERSION,
                locker_fingerprint_id: None,
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                network_token_requestor_reference_id: None,
                external_vault_source: None,
                external_vault_token_data: None,
                vault_type: None,
            },
            storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to add payment method in db")?;

    Ok(response)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn create_payment_method_for_confirm(
    state: &SessionState,
    customer_id: &id_type::GlobalCustomerId,
    payment_method_id: id_type::GlobalPaymentMethodId,
    external_vault_source: Option<id_type::MerchantConnectorAccountId>,
    merchant_id: &id_type::MerchantId,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: enums::MerchantStorageScheme,
    payment_method_type: storage_enums::PaymentMethod,
    payment_method_subtype: storage_enums::PaymentMethodType,
    encrypted_payment_method_billing_address: Option<
        Encryptable<hyperswitch_domain_models::address::Address>,
    >,
    encrypted_payment_method_data: Option<Encryptable<payment_methods::PaymentMethodsData>>,
    encrypted_external_vault_token_data: Option<
        Encryptable<payment_methods::ExternalVaultTokenData>,
    >,
    vault_type: Option<common_enums::VaultType>,
) -> CustomResult<domain::PaymentMethod, errors::ApiErrorResponse> {
    let db = &*state.store;
    let key_manager_state = &state.into();
    let current_time = common_utils::date_time::now();

    let response = db
        .insert_payment_method(
            key_manager_state,
            key_store,
            domain::PaymentMethod {
                customer_id: customer_id.to_owned(),
                merchant_id: merchant_id.to_owned(),
                id: payment_method_id,
                locker_id: None,
                payment_method_type: Some(payment_method_type),
                payment_method_subtype: Some(payment_method_subtype),
                payment_method_data: encrypted_payment_method_data,
                connector_mandate_details: None,
                customer_acceptance: None,
                client_secret: None,
                status: enums::PaymentMethodStatus::Inactive,
                network_transaction_id: None,
                created_at: current_time,
                last_modified: current_time,
                last_used_at: current_time,
                payment_method_billing_address: encrypted_payment_method_billing_address,
                updated_by: None,
                version: common_types::consts::API_VERSION,
                locker_fingerprint_id: None,
                network_token_locker_id: None,
                network_token_payment_method_data: None,
                network_token_requestor_reference_id: None,
                external_vault_source,
                external_vault_token_data: encrypted_external_vault_token_data,
                vault_type,
            },
            storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to add payment method in db")?;

    Ok(response)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn get_external_vault_token(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: enums::MerchantStorageScheme,
    payment_token: String,
    vault_token: domain::VaultToken,
    payment_method_type: &storage_enums::PaymentMethod,
) -> CustomResult<domain::ExternalVaultPaymentMethodData, errors::ApiErrorResponse> {
    let db = &*state.store;

    let pm_token_data =
        utils::retrieve_payment_token_data(state, payment_token, Some(payment_method_type)).await?;

    let payment_method_id = match pm_token_data {
        storage::PaymentTokenData::PermanentCard(card_token_data) => {
            card_token_data.payment_method_id
        }
        storage::PaymentTokenData::TemporaryGeneric(_) => {
            Err(errors::ApiErrorResponse::NotImplemented {
                message: errors::NotImplementedMessage::Reason(
                    "TemporaryGeneric Token not implemented".to_string(),
                ),
            })?
        }
        storage::PaymentTokenData::AuthBankDebit(_) => {
            Err(errors::ApiErrorResponse::NotImplemented {
                message: errors::NotImplementedMessage::Reason(
                    "AuthBankDebit Token not implemented".to_string(),
                ),
            })?
        }
    };

    let payment_method = db
        .find_payment_method(&state.into(), key_store, &payment_method_id, storage_scheme)
        .await
        .change_context(errors::ApiErrorResponse::PaymentMethodNotFound)
        .attach_printable("Payment method not found")?;

    let external_vault_token_data = payment_method
        .external_vault_token_data
        .clone()
        .map(Encryptable::into_inner)
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Missing vault token data")?;

    let decrypted_addtional_payment_method_data = payment_method
        .payment_method_data
        .clone()
        .map(Encryptable::into_inner)
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to convert payment method data")?;

    convert_from_saved_payment_method_data(
        decrypted_addtional_payment_method_data,
        external_vault_token_data,
        vault_token,
    )
    .attach_printable("Failed to convert payment method data")
}

#[cfg(feature = "v2")]
fn convert_from_saved_payment_method_data(
    vault_additional_data: payment_methods::PaymentMethodsData,
    external_vault_token_data: payment_methods::ExternalVaultTokenData,
    vault_token: domain::VaultToken,
) -> RouterResult<domain::ExternalVaultPaymentMethodData> {
    match vault_additional_data {
        payment_methods::PaymentMethodsData::Card(card_details) => {
            Ok(domain::ExternalVaultPaymentMethodData::Card(Box::new(
                domain::ExternalVaultCard {
                    card_number: external_vault_token_data.tokenized_card_number,
                    card_exp_month: card_details.expiry_month.ok_or(
                        errors::ApiErrorResponse::MissingRequiredField {
                            field_name: "card_details.expiry_month",
                        },
                    )?,
                    card_exp_year: card_details.expiry_year.ok_or(
                        errors::ApiErrorResponse::MissingRequiredField {
                            field_name: "card_details.expiry_year",
                        },
                    )?,
                    card_holder_name: card_details.card_holder_name,
                    bin_number: card_details.card_isin,
                    last_four: card_details.last4_digits,
                    nick_name: card_details.nick_name,
                    card_issuing_country: card_details.issuer_country,
                    card_network: card_details.card_network,
                    card_issuer: card_details.card_issuer,
                    card_type: card_details.card_type,
                    card_cvc: vault_token.card_cvc,
                    co_badged_card_data: None, // Co-badged data is not supported in external vault
                    bank_code: None,           // Bank code is not stored in external vault
                },
            )))
        }
        payment_methods::PaymentMethodsData::BankDetails(_)
        | payment_methods::PaymentMethodsData::WalletDetails(_) => {
            Err(errors::ApiErrorResponse::UnprocessableEntity {
                message: "External vaulting is not supported for this payment method type"


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

impl api::RefundExecute for for Datatrans


// Struct: Paytm
// File: crates/hyperswitch_connectors/src/connectors/paytm.rs
// Module: hyperswitch_connectors
// Implementations: 17
// Traits: api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, ConnectorCommon, ConnectorValidation, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Paytm

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


// Function: validate
// File: crates/drainer/src/settings.rs
// Module: drainer

pub fn validate(&self) -> Result<(), errors::DrainerError>


// Struct: WebhookTransformData
// File: crates/router/src/core/unified_connector_service/transformers.rs
// Module: router
// Implementations: 0

pub struct WebhookTransformData


// Struct: ApplepayPaymentMethod
// File: crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ApplepayPaymentMethod


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

pub struct SubmitSingleCaptureResult


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

impl ConnectorCommon for for Katapult


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

// Public submodules:
pub mod transformers;


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

pub struct StripeSetupIntentRequest


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

pub struct PaypalIncrementalAuthResponse


// Implementation: impl ResponseCode
// File: crates/hyperswitch_connectors/src/connectors/peachpayments/transformers.rs
// Module: hyperswitch_connectors
// Methods: 3 total (3 public)

impl ResponseCode


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

pub struct BusinessPaymentLinkConfig


// File: crates/api_models/src/enums.rs
// Module: api_models
// Public functions: 8
// Public structs: 1

use std::str::FromStr;

pub use common_enums::*;
use utoipa::ToSchema;

pub use super::connector_enums::Connector;

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]

/// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RoutingAlgorithm {
    RoundRobin,
    MaxConversion,
    MinCost,
    Custom,
}

#[cfg(feature = "payouts")]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PayoutConnectors {
    Adyen,
    Adyenplatform,
    Cybersource,
    Ebanx,
    Nomupay,
    Nuvei,
    Payone,
    Paypal,
    Stripe,
    Wise,
}

#[cfg(feature = "v2")]
/// Whether active attempt is to be set/unset
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
pub enum UpdateActiveAttempt {
    /// Request to set the active attempt id
    #[schema(value_type = Option<String>)]
    Set(common_utils::id_type::GlobalAttemptId),
    /// To unset the active attempt id
    Unset,
}

#[cfg(feature = "payouts")]
impl From<PayoutConnectors> for RoutableConnectors {
    fn from(value: PayoutConnectors) -> Self {
        match value {
            PayoutConnectors::Adyen => Self::Adyen,
            PayoutConnectors::Adyenplatform => Self::Adyenplatform,
            PayoutConnectors::Cybersource => Self::Cybersource,
            PayoutConnectors::Ebanx => Self::Ebanx,
            PayoutConnectors::Nomupay => Self::Nomupay,
            PayoutConnectors::Nuvei => Self::Nuvei,
            PayoutConnectors::Payone => Self::Payone,
            PayoutConnectors::Paypal => Self::Paypal,
            PayoutConnectors::Stripe => Self::Stripe,
            PayoutConnectors::Wise => Self::Wise,
        }
    }
}

#[cfg(feature = "payouts")]
impl From<PayoutConnectors> for Connector {
    fn from(value: PayoutConnectors) -> Self {
        match value {
            PayoutConnectors::Adyen => Self::Adyen,
            PayoutConnectors::Adyenplatform => Self::Adyenplatform,
            PayoutConnectors::Cybersource => Self::Cybersource,
            PayoutConnectors::Ebanx => Self::Ebanx,
            PayoutConnectors::Nomupay => Self::Nomupay,
            PayoutConnectors::Nuvei => Self::Nuvei,
            PayoutConnectors::Payone => Self::Payone,
            PayoutConnectors::Paypal => Self::Paypal,
            PayoutConnectors::Stripe => Self::Stripe,
            PayoutConnectors::Wise => Self::Wise,
        }
    }
}

#[cfg(feature = "payouts")]
impl TryFrom<Connector> for PayoutConnectors {
    type Error = String;
    fn try_from(value: Connector) -> Result<Self, Self::Error> {
        match value {
            Connector::Adyen => Ok(Self::Adyen),
            Connector::Adyenplatform => Ok(Self::Adyenplatform),
            Connector::Cybersource => Ok(Self::Cybersource),
            Connector::Ebanx => Ok(Self::Ebanx),
            Connector::Nuvei => Ok(Self::Nuvei),
            Connector::Nomupay => Ok(Self::Nomupay),
            Connector::Payone => Ok(Self::Payone),
            Connector::Paypal => Ok(Self::Paypal),
            Connector::Stripe => Ok(Self::Stripe),
            Connector::Wise => Ok(Self::Wise),
            _ => Err(format!("Invalid payout connector {value}")),
        }
    }
}

#[cfg(feature = "frm")]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum FrmConnectors {
    /// Signifyd Risk Manager. Official docs: https://docs.signifyd.com/
    Signifyd,
    Riskified,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum TaxConnectors {
    Taxjar,
}

#[derive(Clone, Debug, serde::Serialize, strum::EnumString, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum BillingConnectors {
    Chargebee,
    Recurly,
    Stripebilling,
    Custombilling,
    #[cfg(feature = "dummy_connector")]
    DummyBillingConnector,
}

#[derive(Clone, Copy, Debug, serde::Serialize, strum::EnumString, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum VaultConnectors {
    Vgs,
    HyperswitchVault,
    Tokenex,
}

impl From<VaultConnectors> for Connector {
    fn from(value: VaultConnectors) -> Self {
        match value {
            VaultConnectors::Vgs => Self::Vgs,
            VaultConnectors::HyperswitchVault => Self::HyperswitchVault,
            VaultConnectors::Tokenex => Self::Tokenex,
        }
    }
}

#[derive(
    Clone, Debug, serde::Deserialize, serde::Serialize, strum::Display, strum::EnumString, ToSchema,
)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum FrmAction {
    CancelTxn,
    AutoRefund,
    ManualReview,
}

#[derive(
    Clone, Debug, serde::Deserialize, serde::Serialize, strum::Display, strum::EnumString, ToSchema,
)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum FrmPreferredFlowTypes {
    Pre,
    Post,
}
#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
pub struct UnresolvedResponseReason {
    pub code: String,
    /// A message to merchant to give hint on next action he/she should do to resolve
    pub message: String,
}

/// Possible field type of required fields in payment_method_data
#[derive(
    Clone,
    Debug,
    Eq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum FieldType {
    UserCardNumber,
    UserCardExpiryMonth,
    UserCardExpiryYear,
    UserCardCvc,
    UserCardNetwork,
    UserFullName,
    UserEmailAddress,
    UserPhoneNumber,
    UserPhoneNumberCountryCode,           //phone number's country code
    UserCountry { options: Vec<String> }, //for country inside payment method data ex- bank redirect
    UserCurrency { options: Vec<String> },
    UserCryptoCurrencyNetwork, //for crypto network associated with the cryptopcurrency
    UserBillingName,
    UserAddressLine1,
    UserAddressLine2,
    UserAddressCity,
    UserAddressPincode,
    UserAddressState,
    UserAddressCountry { options: Vec<String> },
    UserShippingName,
    UserShippingAddressLine1,
    UserShippingAddressLine2,
    UserShippingAddressCity,
    UserShippingAddressPincode,
    UserShippingAddressState,
    UserShippingAddressCountry { options: Vec<String> },
    UserSocialSecurityNumber,
    UserBlikCode,
    UserBank,
    UserBankOptions { options: Vec<String> },
    UserBankAccountNumber,
    UserSourceBankAccountId,
    UserDestinationBankAccountId,
    Text,
    DropDown { options: Vec<String> },
    UserDateOfBirth,
    UserVpaId,
    LanguagePreference { options: Vec<String> },
    UserPixKey,
    UserCpf,
    UserCnpj,
    UserIban,
    UserBsbNumber,
    UserBankSortCode,
    UserBankRoutingNumber,
    UserBankType { options: Vec<String> },
    UserBankAccountHolderName,
    UserMsisdn,
    UserClientIdentifier,
    OrderDetailsProductName,
}

impl FieldType {
    pub fn get_billing_variants() -> Vec<Self> {
        vec![
            Self::UserBillingName,
            Self::UserAddressLine1,
            Self::UserAddressLine2,
            Self::UserAddressCity,
            Self::UserAddressPincode,
            Self::UserAddressState,
            Self::UserAddressCountry { options: vec![] },
        ]
    }

    pub fn get_shipping_variants() -> Vec<Self> {
        vec![
            Self::UserShippingName,
            Self::UserShippingAddressLine1,
            Self::UserShippingAddressLine2,
            Self::UserShippingAddressCity,
            Self::UserShippingAddressPincode,
            Self::UserShippingAddressState,
            Self::UserShippingAddressCountry { options: vec![] },
        ]
    }
}

/// This implementatiobn is to ignore the inner value of UserAddressCountry enum while comparing
impl PartialEq for FieldType {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::UserCardNumber, Self::UserCardNumber) => true,
            (Self::UserCardExpiryMonth, Self::UserCardExpiryMonth) => true,
            (Self::UserCardExpiryYear, Self::UserCardExpiryYear) => true,
            (Self::UserCardCvc, Self::UserCardCvc) => true,
            (Self::UserFullName, Self::UserFullName) => true,
            (Self::UserEmailAddress, Self::UserEmailAddress) => true,
            (Self::UserPhoneNumber, Self::UserPhoneNumber) => true,
            (Self::UserPhoneNumberCountryCode, Self::UserPhoneNumberCountryCode) => true,
            (
                Self::UserCountry {
                    options: options_self,
                },
                Self::UserCountry {
                    options: options_other,
                },
            ) => options_self.eq(options_other),
            (
                Self::UserCurrency {
                    options: options_self,
                },
                Self::UserCurrency {
                    options: options_other,
                },
            ) => options_self.eq(options_other),
            (Self::UserCryptoCurrencyNetwork, Self::UserCryptoCurrencyNetwork) => true,
            (Self::UserBillingName, Self::UserBillingName) => true,
            (Self::UserAddressLine1, Self::UserAddressLine1) => true,
            (Self::UserAddressLine2, Self::UserAddressLine2) => true,
            (Self::UserAddressCity, Self::UserAddressCity) => true,
            (Self::UserAddressPincode, Self::UserAddressPincode) => true,
            (Self::UserAddressState, Self::UserAddressState) => true,
            (Self::UserAddressCountry { .. }, Self::UserAddressCountry { .. }) => true,
            (Self::UserShippingName, Self::UserShippingName) => true,
            (Self::UserShippingAddressLine1, Self::UserShippingAddressLine1) => true,
            (Self::UserShippingAddressLine2, Self::UserShippingAddressLine2) => true,
            (Self::UserShippingAddressCity, Self::UserShippingAddressCity) => true,
            (Self::UserShippingAddressPincode, Self::UserShippingAddressPincode) => true,
            (Self::UserShippingAddressState, Self::UserShippingAddressState) => true,
            (Self::UserShippingAddressCountry { .. }, Self::UserShippingAddressCountry { .. }) => {
                true
            }
            (Self::UserBlikCode, Self::UserBlikCode) => true,
            (Self::UserBank, Self::UserBank) => true,
            (Self::Text, Self::Text) => true,
            (
                Self::DropDown {
                    options: options_self,
                },
                Self::DropDown {
                    options: options_other,
                },
            ) => options_self.eq(options_other),
            (Self::UserDateOfBirth, Self::UserDateOfBirth) => true,
            (Self::UserVpaId, Self::UserVpaId) => true,
            (Self::UserPixKey, Self::UserPixKey) => true,
            (Self::UserCpf, Self::UserCpf) => true,
            (Self::UserCnpj, Self::UserCnpj) => true,
            (Self::LanguagePreference { .. }, Self::LanguagePreference { .. }) => true,
            (Self::UserMsisdn, Self::UserMsisdn) => true,
            (Self::UserClientIdentifier, Self::UserClientIdentifier) => true,
            (Self::OrderDetailsProductName, Self::OrderDetailsProductName) => true,
            _unused => false,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_partialeq_for_field_type() {
        let user_address_country_is_us = FieldType::UserAddressCountry {
            options: vec!["US".to_string()],
        };

        let user_address_country_is_all = FieldType::UserAddressCountry {
            options: vec!["ALL".to_string()],
        };

        assert!(user_address_country_is_us.eq(&user_address_country_is_all))
    }
}

/// Denotes the retry action
#[derive(
    Debug,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    Clone,
    PartialEq,
    Eq,
    ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RetryAction {
    /// Manual retry through request is being deprecated, now it is available through profile
    ManualRetry,
    /// Denotes that the payment is requeued
    Requeue,
}

#[derive(Clone, Copy)]
pub enum LockerChoice {
    HyperswitchCardVault,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PmAuthConnectors {
    Plaid,
}

pub fn convert_pm_auth_connector(connector_name: &str) -> Option<PmAuthConnectors> {
    PmAuthConnectors::from_str(connector_name).ok()
}

pub fn convert_authentication_connector(connector_name: &str) -> Option<AuthenticationConnectors> {
    AuthenticationConnectors::from_str(connector_name).ok()
}

pub fn convert_tax_connector(connector_name: &str) -> Option<TaxConnectors> {
    TaxConnectors::from_str(connector_name).ok()
}

pub fn convert_billing_connector(connector_name: &str) -> Option<BillingConnectors> {
    BillingConnectors::from_str(connector_name).ok()
}
#[cfg(feature = "frm")]
pub fn convert_frm_connector(connector_name: &str) -> Option<FrmConnectors> {
    FrmConnectors::from_str(connector_name).ok()
}

pub fn convert_vault_connector(connector_name: &str) -> Option<VaultConnectors> {
    VaultConnectors::from_str(connector_name).ok()
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, serde::Serialize, Hash)]
pub enum ReconPermissionScope {
    #[serde(rename = "R")]
    Read = 0,
    #[serde(rename = "RW")]
    Write = 1,
}

impl From<PermissionScope> for ReconPermissionScope {
    fn from(scope: PermissionScope) -> Self {
        match scope {
            PermissionScope::Read => Self::Read,
            PermissionScope::Write => Self::Write,
        }
    }
}


// Function: get_paysafe_gift_card_account_id
// File: crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
// Module: hyperswitch_connectors

pub fn get_paysafe_gift_card_account_id(
        &self,
        currency: Currency,
    ) -> Result<Secret<String>, errors::ConnectorError>


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

impl Responder


// File: crates/router/src/services/kafka/refund.rs
// Module: router
// Public functions: 2
// Public structs: 2

#[cfg(feature = "v2")]
use common_utils::pii;
#[cfg(feature = "v2")]
use common_utils::types::{self, ChargeRefunds};
use common_utils::{
    id_type,
    types::{ConnectorTransactionIdTrait, MinorUnit},
};
use diesel_models::{enums as storage_enums, refund::Refund};
use time::OffsetDateTime;

use crate::events;

#[cfg(feature = "v1")]
#[derive(serde::Serialize, Debug)]
pub struct KafkaRefund<'a> {
    pub internal_reference_id: &'a String,
    pub refund_id: &'a String, //merchant_reference id
    pub payment_id: &'a id_type::PaymentId,
    pub merchant_id: &'a id_type::MerchantId,
    pub connector_transaction_id: &'a String,
    pub connector: &'a String,
    pub connector_refund_id: Option<&'a String>,
    pub external_reference_id: Option<&'a String>,
    pub refund_type: &'a storage_enums::RefundType,
    pub total_amount: &'a MinorUnit,
    pub currency: &'a storage_enums::Currency,
    pub refund_amount: &'a MinorUnit,
    pub refund_status: &'a storage_enums::RefundStatus,
    pub sent_to_gateway: &'a bool,
    pub refund_error_message: Option<&'a String>,
    pub refund_arn: Option<&'a String>,
    #[serde(default, with = "time::serde::timestamp")]
    pub created_at: OffsetDateTime,
    #[serde(default, with = "time::serde::timestamp")]
    pub modified_at: OffsetDateTime,
    pub description: Option<&'a String>,
    pub attempt_id: &'a String,
    pub refund_reason: Option<&'a String>,
    pub refund_error_code: Option<&'a String>,
    pub profile_id: Option<&'a id_type::ProfileId>,
    pub organization_id: &'a id_type::OrganizationId,
}

#[cfg(feature = "v1")]
impl<'a> KafkaRefund<'a> {
    pub fn from_storage(refund: &'a Refund) -> Self {
        Self {
            internal_reference_id: &refund.internal_reference_id,
            refund_id: &refund.refund_id,
            payment_id: &refund.payment_id,
            merchant_id: &refund.merchant_id,
            connector_transaction_id: refund.get_connector_transaction_id(),
            connector: &refund.connector,
            connector_refund_id: refund.get_optional_connector_refund_id(),
            external_reference_id: refund.external_reference_id.as_ref(),
            refund_type: &refund.refund_type,
            total_amount: &refund.total_amount,
            currency: &refund.currency,
            refund_amount: &refund.refund_amount,
            refund_status: &refund.refund_status,
            sent_to_gateway: &refund.sent_to_gateway,
            refund_error_message: refund.refund_error_message.as_ref(),
            refund_arn: refund.refund_arn.as_ref(),
            created_at: refund.created_at.assume_utc(),
            modified_at: refund.modified_at.assume_utc(),
            description: refund.description.as_ref(),
            attempt_id: &refund.attempt_id,
            refund_reason: refund.refund_reason.as_ref(),
            refund_error_code: refund.refund_error_code.as_ref(),
            profile_id: refund.profile_id.as_ref(),
            organization_id: &refund.organization_id,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(serde::Serialize, Debug)]
pub struct KafkaRefund<'a> {
    pub refund_id: &'a id_type::GlobalRefundId,
    pub merchant_reference_id: &'a id_type::RefundReferenceId,
    pub payment_id: &'a id_type::GlobalPaymentId,
    pub merchant_id: &'a id_type::MerchantId,
    pub connector_transaction_id: &'a types::ConnectorTransactionId,
    pub connector: &'a String,
    pub connector_refund_id: Option<&'a types::ConnectorTransactionId>,
    pub external_reference_id: Option<&'a String>,
    pub refund_type: &'a storage_enums::RefundType,
    pub total_amount: &'a MinorUnit,
    pub currency: &'a storage_enums::Currency,
    pub refund_amount: &'a MinorUnit,
    pub refund_status: &'a storage_enums::RefundStatus,
    pub sent_to_gateway: &'a bool,
    pub refund_error_message: Option<&'a String>,
    pub refund_arn: Option<&'a String>,
    #[serde(default, with = "time::serde::timestamp")]
    pub created_at: OffsetDateTime,
    #[serde(default, with = "time::serde::timestamp")]
    pub modified_at: OffsetDateTime,
    pub description: Option<&'a String>,
    pub attempt_id: &'a id_type::GlobalAttemptId,
    pub refund_reason: Option<&'a String>,
    pub refund_error_code: Option<&'a String>,
    pub profile_id: Option<&'a id_type::ProfileId>,
    pub organization_id: &'a id_type::OrganizationId,

    pub metadata: Option<&'a pii::SecretSerdeValue>,
    pub updated_by: &'a String,
    pub merchant_connector_id: Option<&'a id_type::MerchantConnectorAccountId>,
    pub charges: Option<&'a ChargeRefunds>,
    pub connector_refund_data: Option<&'a String>,
    pub connector_transaction_data: Option<&'a String>,
    pub split_refunds: Option<&'a common_types::refunds::SplitRefund>,
    pub unified_code: Option<&'a String>,
    pub unified_message: Option<&'a String>,
    pub processor_refund_data: Option<&'a String>,
    pub processor_transaction_data: Option<&'a String>,
}

#[cfg(feature = "v2")]
impl<'a> KafkaRefund<'a> {
    pub fn from_storage(refund: &'a Refund) -> Self {
        let Refund {
            payment_id,
            merchant_id,
            connector_transaction_id,
            connector,
            connector_refund_id,
            external_reference_id,
            refund_type,
            total_amount,
            currency,
            refund_amount,
            refund_status,
            sent_to_gateway,
            refund_error_message,
            metadata,
            refund_arn,
            created_at,
            modified_at,
            description,
            attempt_id,
            refund_reason,
            refund_error_code,
            profile_id,
            updated_by,
            charges,
            organization_id,
            split_refunds,
            unified_code,
            unified_message,
            processor_refund_data,
            processor_transaction_data,
            id,
            merchant_reference_id,
            connector_id,
        } = refund;

        Self {
            refund_id: id,
            merchant_reference_id,
            payment_id,
            merchant_id,
            connector_transaction_id,
            connector,
            connector_refund_id: connector_refund_id.as_ref(),
            external_reference_id: external_reference_id.as_ref(),
            refund_type,
            total_amount,
            currency,
            refund_amount,
            refund_status,
            sent_to_gateway,
            refund_error_message: refund_error_message.as_ref(),
            refund_arn: refund_arn.as_ref(),
            created_at: created_at.assume_utc(),
            modified_at: modified_at.assume_utc(),
            description: description.as_ref(),
            attempt_id,
            refund_reason: refund_reason.as_ref(),
            refund_error_code: refund_error_code.as_ref(),
            profile_id: profile_id.as_ref(),
            organization_id,
            metadata: metadata.as_ref(),
            updated_by,
            merchant_connector_id: connector_id.as_ref(),
            charges: charges.as_ref(),
            connector_refund_data: processor_refund_data.as_ref(),
            connector_transaction_data: processor_transaction_data.as_ref(),
            split_refunds: split_refunds.as_ref(),
            unified_code: unified_code.as_ref(),
            unified_message: unified_message.as_ref(),
            processor_refund_data: processor_refund_data.as_ref(),
            processor_transaction_data: processor_transaction_data.as_ref(),
        }
    }
}

impl super::KafkaMessage for KafkaRefund<'_> {
    #[cfg(feature = "v1")]
    fn key(&self) -> String {
        format!(
            "{}_{}_{}_{}",
            self.merchant_id.get_string_repr(),
            self.payment_id.get_string_repr(),
            self.attempt_id,
            self.refund_id
        )
    }
    #[cfg(feature = "v2")]
    fn key(&self) -> String {
        format!(
            "{}_{}_{}_{}",
            self.merchant_id.get_string_repr(),
            self.payment_id.get_string_repr(),
            self.attempt_id.get_string_repr(),
            self.merchant_reference_id.get_string_repr()
        )
    }

    fn event_type(&self) -> events::EventType {
        events::EventType::Refund
    }
}


// Function: parse_facilitapay_error_response
// File: crates/hyperswitch_connectors/src/connectors/facilitapay/transformers.rs
// Module: hyperswitch_connectors

pub fn parse_facilitapay_error_response(
    res: Response,
    event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError>


// Implementation: impl VaultData
// File: crates/hyperswitch_domain_models/src/payments.rs
// Module: hyperswitch_domain_models
// Methods: 2 total (2 public)

impl VaultData


// Struct: TxnData
// File: crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TxnData


                        "connector",
                        connector.to_owned().unwrap_or("null".to_string()),
                    ),
                    ("merchant_id", merchant_id.clone()),
                ),
            ),
        }
    }
}

pub fn add_apple_pay_payment_status_metrics(
    payment_attempt_status: enums::AttemptStatus,
    apple_pay_flow: Option<domain::ApplePayFlow>,
    connector: Option<String>,
    merchant_id: id_type::MerchantId,
) {
    if payment_attempt_status == enums::AttemptStatus::Charged {
        if let Some(flow) = apple_pay_flow {
            match flow {
                domain::ApplePayFlow::Simplified(_) => {
                    metrics::APPLE_PAY_SIMPLIFIED_FLOW_SUCCESSFUL_PAYMENT.add(
                        1,
                        router_env::metric_attributes!(
                            (
                                "connector",
                                connector.to_owned().unwrap_or("null".to_string()),
                            ),
                            ("merchant_id", merchant_id.clone()),
                        ),
                    )
                }
                domain::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_SUCCESSFUL_PAYMENT
                    .add(
                        1,
                        router_env::metric_attributes!(
                            (
                                "connector",
                                connector.to_owned().unwrap_or("null".to_string()),
                            ),
                            ("merchant_id", merchant_id.clone()),
                        ),
                    ),
            }
        }
    } else if payment_attempt_status == enums::AttemptStatus::Failure {
        if let Some(flow) = apple_pay_flow {
            match flow {
                domain::ApplePayFlow::Simplified(_) => {
                    metrics::APPLE_PAY_SIMPLIFIED_FLOW_FAILED_PAYMENT.add(
                        1,
                        router_env::metric_attributes!(
                            (
                                "connector",
                                connector.to_owned().unwrap_or("null".to_string()),
                            ),
                            ("merchant_id", merchant_id.clone()),
                        ),
                    )
                }
                domain::ApplePayFlow::Manual => metrics::APPLE_PAY_MANUAL_FLOW_FAILED_PAYMENT.add(
                    1,
                    router_env::metric_attributes!(
                        (
                            "connector",
                            connector.to_owned().unwrap_or("null".to_string()),
                        ),
                        ("merchant_id", merchant_id.clone()),
                    ),
                ),
            }
        }
    }
}

pub fn check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata(
    metadata: Option<Value>,
) -> bool {
    let external_three_ds_connector_metadata: Option<ExternalThreeDSConnectorMetadata> = metadata
            .parse_value("ExternalThreeDSConnectorMetadata")
            .map_err(|err| logger::warn!(parsing_error=?err,"Error while parsing ExternalThreeDSConnectorMetadata"))
            .ok();
    external_three_ds_connector_metadata
        .and_then(|metadata| metadata.pull_mechanism_for_external_3ds_enabled)
        .unwrap_or(true)
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
pub async fn trigger_payments_webhook<F, Op, D>(
    merchant_account: domain::MerchantAccount,
    business_profile: domain::Profile,
    key_store: &domain::MerchantKeyStore,
    payment_data: D,
    customer: Option<domain::Customer>,
    state: &SessionState,
    operation: Op,
) -> RouterResult<()>
where
    F: Send + Clone + Sync,
    Op: Debug,
    D: payments_core::OperationSessionGetters<F>,
{
    todo!()
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
pub async fn trigger_payments_webhook<F, Op, D>(
    merchant_context: domain::MerchantContext,
    business_profile: domain::Profile,
    payment_data: D,
    customer: Option<domain::Customer>,
    state: &SessionState,
    operation: Op,
) -> RouterResult<()>
where
    F: Send + Clone + Sync,
    Op: Debug,
    D: payments_core::OperationSessionGetters<F>,
{
    let status = payment_data.get_payment_intent().status;
    let should_trigger_webhook = business_profile
        .get_payment_webhook_statuses()
        .contains(&status);

    if should_trigger_webhook {
        let captures = payment_data
            .get_multiple_capture_data()
            .map(|multiple_capture_data| {
                multiple_capture_data
                    .get_all_captures()
                    .into_iter()
                    .cloned()
                    .collect()
            });
        let payment_id = payment_data.get_payment_intent().get_id().to_owned();
        let payments_response = crate::core::payments::transformers::payments_to_payments_response(
            payment_data,
            captures,
            customer,
            services::AuthFlow::Merchant,
            &state.base_url,
            &operation,
            &state.conf.connector_request_reference_id_config,
            None,
            None,
            None,
        )?;

        let event_type = status.into();

        if let services::ApplicationResponse::JsonWithHeaders((payments_response_json, _)) =
            payments_response
        {
            let cloned_state = state.clone();
            // This spawns this futures in a background thread, the exception inside this future won't affect
            // the current thread and the lifecycle of spawn thread is not handled by runtime.
            // So when server shutdown won't wait for this thread's completion.

            if let Some(event_type) = event_type {
                tokio::spawn(
                    async move {
                        let primary_object_created_at = payments_response_json.created;
                        Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
                            cloned_state,
                            merchant_context.clone(),
                            business_profile,
                            event_type,
                            diesel_models::enums::EventClass::Payments,
                            payment_id.get_string_repr().to_owned(),
                            diesel_models::enums::EventObjectType::PaymentDetails,
                            webhooks::OutgoingWebhookContent::PaymentDetails(Box::new(
                                payments_response_json,
                            )),
                            primary_object_created_at,
                        ))
                        .await
                    }
                    .in_current_span(),
                );
            } else {
                logger::warn!(
                    "Outgoing webhook not sent because of missing event type status mapping"
                );
            }
        }
    }

    Ok(())
}

type Handle<T> = tokio::task::JoinHandle<RouterResult<T>>;

pub async fn flatten_join_error<T>(handle: Handle<T>) -> RouterResult<T> {
    match handle.await {
        Ok(Ok(t)) => Ok(t),
        Ok(Err(err)) => Err(err),
        Err(err) => Err(err)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Join Error"),
    }
}

#[cfg(feature = "v1")]
pub async fn trigger_refund_outgoing_webhook(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    refund: &diesel_models::Refund,
    profile_id: id_type::ProfileId,
) -> RouterResult<()> {
    let refund_status = refund.refund_status;

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

    let should_trigger_webhook = business_profile
        .get_refund_webhook_statuses()
        .contains(&refund_status);

    if should_trigger_webhook {
        let event_type = refund_status.into();
        let refund_response: api_models::refunds::RefundResponse = refund.clone().foreign_into();
        let refund_id = refund_response.refund_id.clone();
        let cloned_state = state.clone();
        let cloned_merchant_context = merchant_context.clone();
        let primary_object_created_at = refund_response.created_at;
        if let Some(outgoing_event_type) = event_type {
            tokio::spawn(
                async move {
                    Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
                        cloned_state,
                        cloned_merchant_context,
                        business_profile,
                        outgoing_event_type,
                        diesel_models::enums::EventClass::Refunds,
                        refund_id.to_string(),
                        diesel_models::enums::EventObjectType::RefundDetails,
                        webhooks::OutgoingWebhookContent::RefundDetails(Box::new(refund_response)),
                        primary_object_created_at,
                    ))
                    .await
                }
                .in_current_span(),
            );
        } else {
            logger::warn!("Outgoing webhook not sent because of missing event type status mapping");
        };
    }
    Ok(())
}

#[cfg(feature = "v2")]
pub async fn trigger_refund_outgoing_webhook(
    state: &SessionState,
    merchant_account: &domain::MerchantAccount,
    refund: &diesel_models::Refund,
    profile_id: id_type::ProfileId,
    key_store: &domain::MerchantKeyStore,
) -> RouterResult<()> {
    todo!()
}

pub fn get_locale_from_header(headers: &actix_web::http::header::HeaderMap) -> String {
    get_header_value_by_key(ACCEPT_LANGUAGE.into(), headers)
        .ok()
        .flatten()
        .map(|val| val.to_string())
        .unwrap_or(common_utils::consts::DEFAULT_LOCALE.to_string())
}

#[cfg(all(feature = "payouts", feature = "v1"))]
pub async fn trigger_payouts_webhook(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payout_response: &api_models::payouts::PayoutCreateResponse,
) -> RouterResult<()> {
    let key_manager_state = &(state).into();
    let profile_id = &payout_response.profile_id;
    let business_profile = state
        .store
        .find_business_profile_by_profile_id(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            profile_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    let status = &payout_response.status;
    let should_trigger_webhook = business_profile
        .get_payout_webhook_statuses()
        .contains(status);

    if should_trigger_webhook {
        let event_type = (*status).into();
        if let Some(event_type) = event_type {
            let cloned_merchant_context = merchant_context.clone();
            let cloned_state = state.clone();
            let cloned_response = payout_response.clone();

            // This spawns this futures in a background thread, the exception inside this future won't affect
            // the current thread and the lifecycle of spawn thread is not handled by runtime.
            // So when server shutdown won't wait for this thread's completion.
            tokio::spawn(
                async move {
                    let primary_object_created_at = cloned_response.created;
                    Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
                        cloned_state,
                        cloned_merchant_context,
                        business_profile,
                        event_type,
                        diesel_models::enums::EventClass::Payouts,
                        cloned_response.payout_id.get_string_repr().to_owned(),
                        diesel_models::enums::EventObjectType::PayoutDetails,
                        webhooks::OutgoingWebhookContent::PayoutDetails(Box::new(cloned_response)),
                        primary_object_created_at,
                    ))
                    .await
                }
                .in_current_span(),
            );
        } else {
            logger::warn!("Outgoing webhook not sent because of missing event type status mapping");
        }
    }
    Ok(())
}

#[cfg(all(feature = "payouts", feature = "v2"))]
pub async fn trigger_payouts_webhook(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payout_response: &api_models::payouts::PayoutCreateResponse,
) -> RouterResult<()> {
    todo!()
}


// Implementation: impl ConnectorPostAuthentication for for Netcetera
// File: crates/hyperswitch_connectors/src/connectors/netcetera.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ConnectorPostAuthentication for for Netcetera


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

impl api::PaymentAuthorize for for Bamboraapac


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

pub fn add_connector_mandate_details_in_payment_method(
    payment_method_type: Option<storage_enums::PaymentMethodType>,
    authorized_amount: Option<i64>,
    authorized_currency: Option<storage_enums::Currency>,
    merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    connector_mandate_id: Option<String>,
    mandate_metadata: Option<Secret<serde_json::Value>>,
    connector_mandate_request_reference_id: Option<String>,
) -> Option<CommonMandateReference>


            shipping_cost: None,
            merchant_account_id: None,
            merchant_config_currency: None,
            connector_testing_data: None,
            order_id: None,
            locale: None,
            payment_channel: None,
            enable_partial_authorization: None,
            enable_overcapture: None,
            is_stored_credential: None,
        };
        Self(data)
    }
}

impl Default for PaymentCaptureType {
    fn default() -> Self {
        Self(types::PaymentsCaptureData {
            amount_to_capture: 100,
            currency: enums::Currency::USD,
            connector_transaction_id: "".to_string(),
            payment_amount: 100,
            ..Default::default()
        })
    }
}

impl Default for PaymentCancelType {
    fn default() -> Self {
        Self(types::PaymentsCancelData {
            cancellation_reason: Some("requested_by_customer".to_string()),
            connector_transaction_id: "".to_string(),
            ..Default::default()
        })
    }
}

impl Default for BrowserInfoType {
    fn default() -> Self {
        let data = types::BrowserInformation {
            user_agent: Some("".to_string()),
            accept_header: Some("".to_string()),
            language: Some("nl-NL".to_string()),
            color_depth: Some(24),
            screen_height: Some(723),
            screen_width: Some(1536),
            time_zone: Some(0),
            java_enabled: Some(true),
            java_script_enabled: Some(true),
            ip_address: Some("127.0.0.1".parse().unwrap()),
            device_model: Some("Apple IPHONE 7".to_string()),
            os_type: Some("IOS or ANDROID".to_string()),
            os_version: Some("IOS 14.5".to_string()),
            accept_language: Some("en".to_string()),
            referer: None,
        };
        Self(data)
    }
}

impl Default for PaymentSyncType {
    fn default() -> Self {
        let data = types::PaymentsSyncData {
            mandate_id: None,
            connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                "12345".to_string(),
            ),
            encoded_data: None,
            capture_method: None,
            sync_type: types::SyncRequestType::SinglePaymentSync,
            connector_meta: None,
            payment_method_type: None,
            currency: enums::Currency::USD,
            payment_experience: None,
            amount: MinorUnit::new(100),
            integrity_object: None,
            ..Default::default()
        };
        Self(data)
    }
}

impl Default for PaymentRefundType {
    fn default() -> Self {
        let data = types::RefundsData {
            payment_amount: 100,
            minor_payment_amount: MinorUnit::new(100),
            currency: enums::Currency::USD,
            refund_id: uuid::Uuid::new_v4().to_string(),
            connector_transaction_id: String::new(),
            refund_amount: 100,
            minor_refund_amount: MinorUnit::new(100),
            webhook_url: None,
            connector_metadata: None,
            refund_connector_metadata: None,
            reason: Some("Customer returned product".to_string()),
            connector_refund_id: None,
            browser_info: None,
            split_refunds: None,
            integrity_object: None,
            refund_status: enums::RefundStatus::Pending,
            merchant_account_id: None,
            merchant_config_currency: None,
            capture_method: None,
            additional_payment_method_data: None,
        };
        Self(data)
    }
}

impl Default for CustomerType {
    fn default() -> Self {
        let data = types::ConnectorCustomerData {
            payment_method_data: Some(types::domain::PaymentMethodData::Card(
                CCardType::default().0,
            )),
            description: None,
            email: Email::from_str("test@juspay.in").ok(),
            phone: None,
            name: None,
            preprocessing_id: None,
            split_payments: None,
            customer_acceptance: None,
            setup_future_usage: None,
            customer_id: None,
            billing_address: None,
        };
        Self(data)
    }
}

impl Default for TokenType {
    fn default() -> Self {
        let data = types::PaymentMethodTokenizationData {
            payment_method_data: types::domain::PaymentMethodData::Card(CCardType::default().0),
            browser_info: None,
            amount: Some(100),
            currency: enums::Currency::USD,
            split_payments: None,
            mandate_id: None,
            setup_future_usage: None,
            customer_acceptance: None,
            setup_mandate_details: None,
        };
        Self(data)
    }
}

pub fn get_connector_transaction_id(
    response: Result<types::PaymentsResponseData, types::ErrorResponse>,
) -> Option<String> {
    match response {
        Ok(types::PaymentsResponseData::TransactionResponse { resource_id, .. }) => {
            resource_id.get_connector_transaction_id().ok()
        }
        Ok(types::PaymentsResponseData::SessionResponse { .. }) => None,
        Ok(types::PaymentsResponseData::SessionTokenResponse { .. }) => None,
        Ok(types::PaymentsResponseData::TokenizationResponse { .. }) => None,
        Ok(types::PaymentsResponseData::TransactionUnresolvedResponse { .. }) => None,
        Ok(types::PaymentsResponseData::PreProcessingResponse { .. }) => None,
        Ok(types::PaymentsResponseData::ConnectorCustomerResponse(..)) => None,
        Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. }) => None,
        Ok(types::PaymentsResponseData::MultipleCaptureResponse { .. }) => None,
        Ok(types::PaymentsResponseData::IncrementalAuthorizationResponse { .. }) => None,
        Ok(types::PaymentsResponseData::PostProcessingResponse { .. }) => None,
        Ok(types::PaymentsResponseData::PaymentResourceUpdateResponse { .. }) => None,
        Ok(types::PaymentsResponseData::PaymentsCreateOrderResponse { .. }) => None,
        Err(_) => None,
    }
}

pub fn get_connector_metadata(
    response: Result<types::PaymentsResponseData, types::ErrorResponse>,
) -> Option<serde_json::Value> {
    match response {
        Ok(types::PaymentsResponseData::TransactionResponse {
            resource_id: _,
            redirection_data: _,
            mandate_reference: _,
            connector_metadata,
            network_txn_id: _,
            connector_response_reference_id: _,
            incremental_authorization_allowed: _,
            charges: _,
        }) => connector_metadata,
        _ => None,
    }
}

pub fn to_connector_auth_type(auth_type: ConnectorAuthType) -> types::ConnectorAuthType {
    match auth_type {
        ConnectorAuthType::HeaderKey { api_key } => types::ConnectorAuthType::HeaderKey { api_key },
        ConnectorAuthType::BodyKey { api_key, key1 } => {
            types::ConnectorAuthType::BodyKey { api_key, key1 }
        }
        ConnectorAuthType::SignatureKey {
            api_key,
            key1,
            api_secret,
        } => types::ConnectorAuthType::SignatureKey {
            api_key,
            key1,
            api_secret,
        },
        ConnectorAuthType::MultiAuthKey {
            api_key,
            key1,
            api_secret,
            key2,
        } => types::ConnectorAuthType::MultiAuthKey {
            api_key,
            key1,
            api_secret,
            key2,
        },
        _ => types::ConnectorAuthType::NoKey,
    }
}


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

pub fn do_gsm_actions<F, ApiRequest, FData, D>(
    state: &app::SessionState,
    req_state: ReqState,
    payment_data: &mut D,
    mut connector_routing_data: IntoIter<api::ConnectorRoutingData>,
    original_connector_data: &api::ConnectorData,
    mut router_data: types::RouterData<F, FData, types::PaymentsResponseData>,
    merchant_context: &domain::MerchantContext,
    operation: &operations::BoxedOperation<'_, F, ApiRequest, D>,
    customer: &Option<domain::Customer>,
    validate_result: &operations::ValidateResult,
    schedule_time: Option<time::PrimitiveDateTime>,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
) -> RouterResult<types::RouterData<F, FData, types::PaymentsResponseData>>
where
    F: Clone + Send + Sync,
    FData: Send + Sync + types::Capturable,
    payments::PaymentResponse: operations::Operation<F, FData>,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
    D: ConstructFlowSpecificData<F, FData, types::PaymentsResponseData>,
    types::RouterData<F, FData, types::PaymentsResponseData>: Feature<F, FData>,
    dyn api::Connector: services::api::ConnectorIntegration<F, FData, types::PaymentsResponseData>,


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

impl ConnectorCommon for for Wellsfargopayout


// Function: success_based_routing_update_configs
// File: crates/router/src/routes/routing.rs
// Module: router

pub fn success_based_routing_update_configs(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<routing_types::DynamicRoutingUpdateConfigQuery>,
    json_payload: web::Json<routing_types::SuccessBasedRoutingConfig>,
) -> impl Responder


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

pub fn mk_tokenization_req(
    state: &routes::SessionState,
    payload_bytes: &[u8],
    customer_id: id_type::CustomerId,
    tokenization_service: &settings::NetworkTokenizationService,
) -> CustomResult<
    (pm_types::CardNetworkTokenResponsePayload, Option<String>),
    errors::NetworkTokenizationError,
>