// File: crates/router/src/core/payments/helpers.rs
// Module: router
// Public functions: 143
// Public structs: 9

use std::{borrow::Cow, collections::HashSet, net::IpAddr, ops::Deref, str::FromStr};

pub use ::payment_methods::helpers::{
    populate_bin_details_for_payment_method_create,
    validate_payment_method_type_against_payment_method,
};
#[cfg(feature = "v2")]
use api_models::ephemeral_key::ClientSecretResponse;
use api_models::{
    mandates::RecurringDetails,
    payments::{additional_info as payment_additional_types, RequestSurchargeDetails},
};
use base64::Engine;
use common_enums::ConnectorType;
#[cfg(feature = "v2")]
use common_utils::id_type::GenerateId;
use common_utils::{
    crypto::Encryptable,
    ext_traits::{AsyncExt, ByteSliceExt, Encode, ValueExt},
    fp_utils, generate_id,
    id_type::{self},
    new_type::{MaskedIban, MaskedSortCode},
    pii, type_name,
    types::{
        keymanager::{Identifier, KeyManagerState, ToEncryptable},
        MinorUnit,
    },
};
use diesel_models::enums;
// TODO : Evaluate all the helper functions ()
use error_stack::{report, ResultExt};
use futures::future::Either;
#[cfg(feature = "v1")]
use hyperswitch_domain_models::payments::payment_intent::CustomerData;
use hyperswitch_domain_models::{
    mandates::MandateData,
    payment_method_data::{GetPaymentMethodType, PazeWalletData},
    payments::{
        self as domain_payments, payment_attempt::PaymentAttempt,
        payment_intent::PaymentIntentFetchConstraints, PaymentIntent,
    },
    router_data::KlarnaSdkResponse,
};
use hyperswitch_interfaces::integrity::{CheckIntegrity, FlowIntegrity, GetIntegrityObject};
use josekit::jwe;
use masking::{ExposeInterface, PeekInterface, SwitchStrategy};
use num_traits::{FromPrimitive, ToPrimitive};
use openssl::{
    derive::Deriver,
    pkey::PKey,
    symm::{decrypt_aead, Cipher},
};
use rand::Rng;
#[cfg(feature = "v2")]
use redis_interface::errors::RedisError;
use router_env::{instrument, logger, tracing};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use x509_parser::parse_x509_certificate;

use super::{
    operations::{BoxedOperation, Operation, PaymentResponse},
    CustomerDetails, PaymentData,
};
use crate::{
    configs::settings::{ConnectorRequestReferenceIdConfig, TempLockerEnableConfig},
    connector,
    consts::{self, BASE64_ENGINE},
    core::{
        authentication,
        errors::{self, CustomResult, RouterResult, StorageErrorExt},
        mandate::helpers::MandateGenericData,
        payment_methods::{
            self,
            cards::{self},
            network_tokenization, vault,
        },
        payments,
        pm_auth::retrieve_payment_method_from_auth_service,
    },
    db::StorageInterface,
    routes::{metrics, payment_methods as payment_methods_handler, SessionState},
    services,
    types::{
        api::{self, admin, enums as api_enums, MandateValidationFieldsExt},
        domain::{self, types},
        storage::{self, enums as storage_enums, ephemeral_key, CardTokenData},
        transformers::{ForeignFrom, ForeignTryFrom},
        AdditionalMerchantData, AdditionalPaymentMethodConnectorResponse, ErrorResponse,
        MandateReference, MerchantAccountData, MerchantRecipientData, PaymentsResponseData,
        RecipientIdType, RecurringMandatePaymentData, RouterData,
    },
    utils::{
        self,
        crypto::{self, SignMessage},
        OptionExt, StringExt,
    },
};
#[cfg(feature = "v2")]
use crate::{core::admin as core_admin, headers, types::ConnectorAuthType};
#[cfg(feature = "v1")]
use crate::{
    core::payment_methods::cards::create_encrypted_data, types::storage::CustomerUpdate::Update,
};

#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn create_or_update_address_for_payment_by_request(
    session_state: &SessionState,
    req_address: Option<&api::Address>,
    address_id: Option<&str>,
    merchant_id: &id_type::MerchantId,
    customer_id: Option<&id_type::CustomerId>,
    merchant_key_store: &domain::MerchantKeyStore,
    payment_id: &id_type::PaymentId,
    storage_scheme: storage_enums::MerchantStorageScheme,
) -> CustomResult<Option<domain::Address>, errors::ApiErrorResponse> {
    let key = merchant_key_store.key.get_inner().peek();
    let db = &session_state.store;
    let key_manager_state = &session_state.into();
    Ok(match address_id {
        Some(id) => match req_address {
            Some(address) => {
                let encrypted_data = types::crypto_operation(
                    &session_state.into(),
                    type_name!(domain::Address),
                    types::CryptoOperation::BatchEncrypt(
                        domain::FromRequestEncryptableAddress::to_encryptable(
                            domain::FromRequestEncryptableAddress {
                                line1: address.address.as_ref().and_then(|a| a.line1.clone()),
                                line2: address.address.as_ref().and_then(|a| a.line2.clone()),
                                line3: address.address.as_ref().and_then(|a| a.line3.clone()),
                                state: address.address.as_ref().and_then(|a| a.state.clone()),
                                first_name: address
                                    .address
                                    .as_ref()
                                    .and_then(|a| a.first_name.clone()),
                                last_name: address
                                    .address
                                    .as_ref()
                                    .and_then(|a| a.last_name.clone()),
                                zip: address.address.as_ref().and_then(|a| a.zip.clone()),
                                phone_number: address
                                    .phone
                                    .as_ref()
                                    .and_then(|phone| phone.number.clone()),
                                email: address
                                    .email
                                    .as_ref()
                                    .map(|a| a.clone().expose().switch_strategy()),
                                origin_zip: address
                                    .address
                                    .as_ref()
                                    .and_then(|a| a.origin_zip.clone()),
                            },
                        ),
                    ),
                    Identifier::Merchant(merchant_key_store.merchant_id.clone()),
                    key,
                )
                .await
                .and_then(|val| val.try_into_batchoperation())
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed while encrypting address")?;
                let encryptable_address =
                    domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed while encrypting address")?;
                let address_update = storage::AddressUpdate::Update {
                    city: address
                        .address
                        .as_ref()
                        .and_then(|value| value.city.clone()),
                    country: address.address.as_ref().and_then(|value| value.country),
                    line1: encryptable_address.line1,
                    line2: encryptable_address.line2,
                    line3: encryptable_address.line3,
                    state: encryptable_address.state,
                    zip: encryptable_address.zip,
                    first_name: encryptable_address.first_name,
                    last_name: encryptable_address.last_name,
                    phone_number: encryptable_address.phone_number,
                    country_code: address
                        .phone
                        .as_ref()
                        .and_then(|value| value.country_code.clone()),
                    updated_by: storage_scheme.to_string(),
                    email: encryptable_address.email.map(|email| {
                        let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
                            Encryptable::new(
                                email.clone().into_inner().switch_strategy(),
                                email.into_encrypted(),
                            );
                        encryptable
                    }),
                    origin_zip: encryptable_address.origin_zip,
                };
                let address = db
                    .find_address_by_merchant_id_payment_id_address_id(
                        key_manager_state,
                        merchant_id,
                        payment_id,
                        id,
                        merchant_key_store,
                        storage_scheme,
                    )
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Error while fetching address")?;
                Some(
                    db.update_address_for_payments(
                        key_manager_state,
                        address,
                        address_update,
                        payment_id.to_owned(),
                        merchant_key_store,
                        storage_scheme,
                    )
                    .await
                    .map(|payment_address| payment_address.address)
                    .to_not_found_response(errors::ApiErrorResponse::AddressNotFound)?,
                )
            }
            None => Some(
                db.find_address_by_merchant_id_payment_id_address_id(
                    key_manager_state,
                    merchant_id,
                    payment_id,
                    id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await
                .map(|payment_address| payment_address.address),
            )
            .transpose()
            .to_not_found_response(errors::ApiErrorResponse::AddressNotFound)?,
        },
        None => match req_address {
            Some(address) => {
                let address =
                    get_domain_address(session_state, address, merchant_id, key, storage_scheme)
                        .await
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed while encrypting address while insert")?;

                let payment_address = domain::PaymentAddress {
                    address,
                    payment_id: payment_id.clone(),
                    customer_id: customer_id.cloned(),
                };

                Some(
                    db.insert_address_for_payments(
                        key_manager_state,
                        payment_id,
                        payment_address,
                        merchant_key_store,
                        storage_scheme,
                    )
                    .await
                    .map(|payment_address| payment_address.address)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while inserting new address")?,
                )
            }

            None => None,
        },
    })
}

#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn create_or_find_address_for_payment_by_request(
    state: &SessionState,
    req_address: Option<&api::Address>,
    address_id: Option<&str>,
    merchant_id: &id_type::MerchantId,
    customer_id: Option<&id_type::CustomerId>,
    merchant_key_store: &domain::MerchantKeyStore,
    payment_id: &id_type::PaymentId,
    storage_scheme: storage_enums::MerchantStorageScheme,
) -> CustomResult<Option<domain::Address>, errors::ApiErrorResponse> {
    let key = merchant_key_store.key.get_inner().peek();
    let db = &state.store;
    let key_manager_state = &state.into();
    Ok(match address_id {
        Some(id) => Some(
            db.find_address_by_merchant_id_payment_id_address_id(
                key_manager_state,
                merchant_id,
                payment_id,
                id,
                merchant_key_store,
                storage_scheme,
            )
            .await
            .map(|payment_address| payment_address.address),
        )
        .transpose()
        .to_not_found_response(errors::ApiErrorResponse::AddressNotFound)?,
        None => match req_address {
            Some(address) => {
                // generate a new address here
                let address = get_domain_address(state, address, merchant_id, key, storage_scheme)
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while encrypting address while insert")?;

                let payment_address = domain::PaymentAddress {
                    address,
                    payment_id: payment_id.clone(),
                    customer_id: customer_id.cloned(),
                };

                Some(
                    db.insert_address_for_payments(
                        key_manager_state,
                        payment_id,
                        payment_address,
                        merchant_key_store,
                        storage_scheme,
                    )
                    .await
                    .map(|payment_address| payment_address.address)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while inserting new address")?,
                )
            }
            None => None,
        },
    })
}

pub async fn get_domain_address(
    session_state: &SessionState,
    address: &api_models::payments::Address,
    merchant_id: &id_type::MerchantId,
    key: &[u8],
    storage_scheme: enums::MerchantStorageScheme,
) -> CustomResult<domain::Address, common_utils::errors::CryptoError> {
    async {
        let address_details = &address.address.as_ref();
        let encrypted_data = types::crypto_operation(
            &session_state.into(),
            type_name!(domain::Address),
            types::CryptoOperation::BatchEncrypt(
                domain::FromRequestEncryptableAddress::to_encryptable(
                    domain::FromRequestEncryptableAddress {
                        line1: address.address.as_ref().and_then(|a| a.line1.clone()),
                        line2: address.address.as_ref().and_then(|a| a.line2.clone()),
                        line3: address.address.as_ref().and_then(|a| a.line3.clone()),
                        state: address.address.as_ref().and_then(|a| a.state.clone()),
                        first_name: address.address.as_ref().and_then(|a| a.first_name.clone()),
                        last_name: address.address.as_ref().and_then(|a| a.last_name.clone()),
                        zip: address.address.as_ref().and_then(|a| a.zip.clone()),
                        phone_number: address
                            .phone
                            .as_ref()
                            .and_then(|phone| phone.number.clone()),
                        email: address
                            .email
                            .as_ref()
                            .map(|a| a.clone().expose().switch_strategy()),
                        origin_zip: address.address.as_ref().and_then(|a| a.origin_zip.clone()),
                    },
                ),
            ),
            Identifier::Merchant(merchant_id.to_owned()),
            key,
        )
        .await
        .and_then(|val| val.try_into_batchoperation())?;
        let encryptable_address =
            domain::FromRequestEncryptableAddress::from_encryptable(encrypted_data)
                .change_context(common_utils::errors::CryptoError::EncodingFailed)?;
        Ok(domain::Address {
            phone_number: encryptable_address.phone_number,
            country_code: address.phone.as_ref().and_then(|a| a.country_code.clone()),
            merchant_id: merchant_id.to_owned(),
            address_id: generate_id(consts::ID_LENGTH, "add"),
            city: address_details.and_then(|address_details| address_details.city.clone()),
            country: address_details.and_then(|address_details| address_details.country),
            line1: encryptable_address.line1,
            line2: encryptable_address.line2,
            line3: encryptable_address.line3,
            state: encryptable_address.state,
            created_at: common_utils::date_time::now(),
            first_name: encryptable_address.first_name,
            last_name: encryptable_address.last_name,
            modified_at: common_utils::date_time::now(),
            zip: encryptable_address.zip,
            updated_by: storage_scheme.to_string(),
            email: encryptable_address.email.map(|email| {
                let encryptable: Encryptable<masking::Secret<String, pii::EmailStrategy>> =
                    Encryptable::new(
                        email.clone().into_inner().switch_strategy(),
                        email.into_encrypted(),
                    );
                encryptable
            }),
            origin_zip: encryptable_address.origin_zip,
        })
    }
    .await
}

pub async fn get_address_by_id(
    state: &SessionState,
    address_id: Option<String>,
    merchant_key_store: &domain::MerchantKeyStore,
    payment_id: &id_type::PaymentId,
    merchant_id: &id_type::MerchantId,
    storage_scheme: storage_enums::MerchantStorageScheme,
) -> CustomResult<Option<domain::Address>, errors::ApiErrorResponse> {
    match address_id {
        None => Ok(None),
        Some(address_id) => {
            let db = &*state.store;
            Ok(db
                .find_address_by_merchant_id_payment_id_address_id(
                    &state.into(),
                    merchant_id,
                    payment_id,
                    &address_id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await
                .map(|payment_address| payment_address.address)
                .ok())
        }
    }
}

#[cfg(feature = "v1")]
pub async fn get_token_pm_type_mandate_details(
    state: &SessionState,
    request: &api::PaymentsRequest,
    mandate_type: Option<api::MandateTransactionType>,
    merchant_context: &domain::MerchantContext,
    payment_method_id: Option<String>,
    payment_intent_customer_id: Option<&id_type::CustomerId>,
) -> RouterResult<MandateGenericData> {
    let mandate_data = request.mandate_data.clone().map(MandateData::foreign_from);
    let (
        payment_token,
        payment_method,
        payment_method_type,
        mandate_data,
        recurring_payment_data,
        mandate_connector_details,
        payment_method_info,
    ) = match mandate_type {
        Some(api::MandateTransactionType::NewMandateTransaction) => (
            request.payment_token.to_owned(),
            request.payment_method,
            request.payment_method_type,
            mandate_data.clone(),
            None,
            None,
            None,
        ),
        Some(api::MandateTransactionType::RecurringMandateTransaction) => {
            match &request.recurring_details {
                Some(recurring_details) => {
                    match recurring_details {
                        RecurringDetails::NetworkTransactionIdAndCardDetails(_) => {
                            (None, request.payment_method, None, None, None, None, None)
                        }
                        RecurringDetails::ProcessorPaymentToken(processor_payment_token) => {
                            if let Some(mca_id) = &processor_payment_token.merchant_connector_id {
                                let db = &*state.store;
                                let key_manager_state = &state.into();

                                #[cfg(feature = "v1")]
                            let connector_name = db
                                .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
                                    key_manager_state,
                                    merchant_context.get_merchant_account().get_id(),
                                    mca_id,
                                    merchant_context.get_merchant_key_store(),
                                )
                                .await
                                .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                                    id: mca_id.clone().get_string_repr().to_string(),
                                })?.connector_name;

                                #[cfg(feature = "v2")]
                            let connector_name = db
                                .find_merchant_connector_account_by_id(key_manager_state, mca_id, merchant_key_store)
                                .await
                                .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                                    id: mca_id.clone().get_string_repr().to_string(),
                                })?.connector_name;
                                (
                                    None,
                                    request.payment_method,
                                    None,
                                    None,
                                    None,
                                    Some(payments::MandateConnectorDetails {
                                        connector: connector_name,
                                        merchant_connector_id: Some(mca_id.clone()),
                                    }),
                                    None,
                                )
                            } else {
                                (None, request.payment_method, None, None, None, None, None)
                            }
                        }
                        RecurringDetails::MandateId(mandate_id) => {
                            let mandate_generic_data = Box::pin(get_token_for_recurring_mandate(
                                state,
                                request,
                                merchant_context,
                                mandate_id.to_owned(),
                            ))
                            .await?;

                            (
                                mandate_generic_data.token,
                                mandate_generic_data.payment_method,
                                mandate_generic_data
                                    .payment_method_type
                                    .or(request.payment_method_type),
                                None,
                                mandate_generic_data.recurring_mandate_payment_data,
                                mandate_generic_data.mandate_connector,
                                mandate_generic_data.payment_method_info,
                            )
                        }
                        RecurringDetails::PaymentMethodId(payment_method_id) => {
                            let payment_method_info = state
                                .store
                                .find_payment_method(
                                    &(state.into()),
                                    merchant_context.get_merchant_key_store(),
                                    payment_method_id,
                                    merchant_context.get_merchant_account().storage_scheme,
                                )
                                .await
                                .to_not_found_response(
                                    errors::ApiErrorResponse::PaymentMethodNotFound,
                                )?;
                            let customer_id = request
                                .get_customer_id()
                                .get_required_value("customer_id")?;

                            verify_mandate_details_for_recurring_payments(
                                &payment_method_info.merchant_id,
                                merchant_context.get_merchant_account().get_id(),
                                &payment_method_info.customer_id,
                                customer_id,
                            )?;

                            (
                                None,
                                payment_method_info.get_payment_method_type(),
                                payment_method_info.get_payment_method_subtype(),
                                None,
                                None,
                                None,
                                Some(payment_method_info),
                            )
                        }
                    }
                }
                None => {
                    if let Some(mandate_id) = request.mandate_id.clone() {
                        let mandate_generic_data = Box::pin(get_token_for_recurring_mandate(
                            state,
                            request,
                            merchant_context,
                            mandate_id,
                        ))
                        .await?;
                        (
                            mandate_generic_data.token,
                            mandate_generic_data.payment_method,
                            mandate_generic_data
                                .payment_method_type
                                .or(request.payment_method_type),
                            None,
                            mandate_generic_data.recurring_mandate_payment_data,
                            mandate_generic_data.mandate_connector,
                            mandate_generic_data.payment_method_info,
                        )
                    } else if request
                        .payment_method_type
                        .map(|payment_method_type_value| {
                            payment_method_type_value
                                .should_check_for_customer_saved_payment_method_type()
                        })
                        .unwrap_or(false)
                    {
                        let payment_request_customer_id = request.get_customer_id();
                        if let Some(customer_id) =
                            payment_request_customer_id.or(payment_intent_customer_id)
                        {
                            let customer_saved_pm_option = match state
                                .store
                                .find_payment_method_by_customer_id_merchant_id_list(
                                    &(state.into()),
                                    merchant_context.get_merchant_key_store(),
                                    customer_id,
                                    merchant_context.get_merchant_account().get_id(),
                                    None,
                                )
                                .await
                            {
                                Ok(customer_payment_methods) => Ok(customer_payment_methods
                                    .iter()
                                    .find(|payment_method| {
                                        payment_method.get_payment_method_subtype()
                                            == request.payment_method_type
                                    })
                                    .cloned()),
                                Err(error) => {
                                    if error.current_context().is_db_not_found() {
                                        Ok(None)
                                    } else {
                                        Err(error)
                                            .change_context(
                                                errors::ApiErrorResponse::InternalServerError,
                                            )
                                            .attach_printable(
                                                "failed to find payment methods for a customer",
                                            )
                                    }
                                }
                            }?;

                            (
                                None,
                                request.payment_method,
                                request.payment_method_type,
                                None,
                                None,
                                None,
                                customer_saved_pm_option,
                            )
                        } else {
                            (
                                None,
                                request.payment_method,
                                request.payment_method_type,
                                None,
                                None,
                                None,
                                None,
                            )
                        }
                    } else {
                        let payment_method_info = payment_method_id
                            .async_map(|payment_method_id| async move {
                                state
                                    .store
                                    .find_payment_method(
                                        &(state.into()),
                                        merchant_context.get_merchant_key_store(),
                                        &payment_method_id,
                                        merchant_context.get_merchant_account().storage_scheme,
                                    )
                                    .await
                                    .to_not_found_response(
                                        errors::ApiErrorResponse::PaymentMethodNotFound,
                                    )
                            })
                            .await
                            .transpose()?;
                        (
                            request.payment_token.to_owned(),
                            request.payment_method,
                            request.payment_method_type,
                            None,
                            None,
                            None,
                            payment_method_info,
                        )
                    }
                }
            }
        }
        None => {
            let payment_method_info = payment_method_id
                .async_map(|payment_method_id| async move {
                    state
                        .store
                        .find_payment_method(
                            &(state.into()),
                            merchant_context.get_merchant_key_store(),
                            &payment_method_id,
                            merchant_context.get_merchant_account().storage_scheme,
                        )
                        .await
                        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)
                })
                .await
                .transpose()?;
            (
                request.payment_token.to_owned(),
                request.payment_method,
                request.payment_method_type,
                mandate_data,
                None,
                None,
                payment_method_info,
            )
        }
    };
    Ok(MandateGenericData {
        token: payment_token,
        payment_method,
        payment_method_type,
        mandate_data,
        recurring_mandate_payment_data: recurring_payment_data,
        mandate_connector: mandate_connector_details,
        payment_method_info,
    })
}

#[cfg(feature = "v1")]
pub async fn get_token_for_recurring_mandate(
    state: &SessionState,
    req: &api::PaymentsRequest,
    merchant_context: &domain::MerchantContext,
    mandate_id: String,
) -> RouterResult<MandateGenericData> {
    let db = &*state.store;

    let mandate = db
        .find_mandate_by_merchant_id_mandate_id(
            merchant_context.get_merchant_account().get_id(),
            mandate_id.as_str(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
    let key_manager_state: KeyManagerState = state.into();
    let original_payment_intent = mandate
        .original_payment_id
        .as_ref()
        .async_map(|payment_id| async {
            db.find_payment_intent_by_payment_id_merchant_id(
                &key_manager_state,
                payment_id,
                &mandate.merchant_id,
                merchant_context.get_merchant_key_store(),
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
            .map_err(|err| logger::error!(mandate_original_payment_not_found=?err))
            .ok()
        })
        .await
        .flatten();

    let original_payment_attempt = original_payment_intent
        .as_ref()
        .async_map(|payment_intent| async {
            db.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_intent.payment_id,
                &mandate.merchant_id,
                payment_intent.active_attempt.get_id().as_str(),
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
            .map_err(|err| logger::error!(mandate_original_payment_attempt_not_found=?err))
            .ok()
        })
        .await
        .flatten();

    let original_payment_authorized_amount = original_payment_attempt
        .clone()
        .map(|pa| pa.net_amount.get_total_amount().get_amount_as_i64());
    let original_payment_authorized_currency =
        original_payment_intent.clone().and_then(|pi| pi.currency);
    let customer = req.get_customer_id().get_required_value("customer_id")?;

    let payment_method_id = {
        if &mandate.customer_id != customer {
            Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                message: "customer_id must match mandate customer_id".into()
            }))?
        }
        if mandate.mandate_status != storage_enums::MandateStatus::Active {
            Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                message: "mandate is not active".into()
            }))?
        };
        mandate.payment_method_id.clone()
    };
    verify_mandate_details(
        req.amount.get_required_value("amount")?.into(),
        req.currency.get_required_value("currency")?,
        mandate.clone(),
    )?;

    let payment_method = db
        .find_payment_method(
            &(state.into()),
            merchant_context.get_merchant_key_store(),
            payment_method_id.as_str(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

    let token = Uuid::new_v4().to_string();
    let payment_method_type = payment_method.get_payment_method_subtype();
    let mandate_connector_details = payments::MandateConnectorDetails {
        connector: mandate.connector,
        merchant_connector_id: mandate.merchant_connector_id,
    };

    if let Some(enums::PaymentMethod::Card) = payment_method.get_payment_method_type() {
        if state.conf.locker.locker_enabled {
            let _ = cards::get_lookup_key_from_locker(
                state,
                &token,
                &payment_method,
                merchant_context.get_merchant_key_store(),
            )
            .await?;
        }

        if let Some(payment_method_from_request) = req.payment_method {
            let pm: storage_enums::PaymentMethod = payment_method_from_request;
            if payment_method
                .get_payment_method_type()
                .is_some_and(|payment_method| payment_method != pm)
            {
                Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                    message:
                        "payment method in request does not match previously provided payment \
                            method information"
                            .into()
                }))?
            }
        };

        Ok(MandateGenericData {
            token: Some(token),
            payment_method: payment_method.get_payment_method_type(),
            recurring_mandate_payment_data: Some(RecurringMandatePaymentData {
                payment_method_type,
                original_payment_authorized_amount,
                original_payment_authorized_currency,
                mandate_metadata: None,
            }),
            payment_method_type: payment_method.get_payment_method_subtype(),
            mandate_connector: Some(mandate_connector_details),
            mandate_data: None,
            payment_method_info: Some(payment_method),
        })
    } else {
        Ok(MandateGenericData {
            token: None,
            payment_method: payment_method.get_payment_method_type(),
            recurring_mandate_payment_data: Some(RecurringMandatePaymentData {
                payment_method_type,
                original_payment_authorized_amount,
                original_payment_authorized_currency,
                mandate_metadata: None,
            }),
            payment_method_type: payment_method.get_payment_method_subtype(),
            mandate_connector: Some(mandate_connector_details),
            mandate_data: None,
            payment_method_info: Some(payment_method),
        })
    }
}

#[instrument(skip_all)]
/// Check weather the merchant id in the request
/// and merchant id in the merchant account are same.
pub fn validate_merchant_id(
    merchant_id: &id_type::MerchantId,
    request_merchant_id: Option<&id_type::MerchantId>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    // Get Merchant Id from the merchant
    // or get from merchant account

    let request_merchant_id = request_merchant_id.unwrap_or(merchant_id);

    utils::when(merchant_id.ne(request_merchant_id), || {
        Err(report!(errors::ApiErrorResponse::PreconditionFailed {
            message: format!(
                "Invalid `merchant_id`: {} not found in merchant account",
                request_merchant_id.get_string_repr()
            )
        }))
    })
}

#[instrument(skip_all)]
pub fn validate_request_amount_and_amount_to_capture(
    op_amount: Option<api::Amount>,
    op_amount_to_capture: Option<MinorUnit>,
    surcharge_details: Option<RequestSurchargeDetails>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    match (op_amount, op_amount_to_capture) {
        (None, _) => Ok(()),
        (Some(_amount), None) => Ok(()),
        (Some(amount), Some(amount_to_capture)) => {
            match amount {
                api::Amount::Value(amount_inner) => {
                    // If both amount and amount to capture is present
                    // then amount to be capture should be less than or equal to request amount
                    let total_capturable_amount = MinorUnit::new(amount_inner.get())
                        + surcharge_details
                            .map(|surcharge_details| surcharge_details.get_total_surcharge_amount())
                            .unwrap_or_default();
                    utils::when(!amount_to_capture.le(&total_capturable_amount), || {
                        Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                            message: format!(
                            "amount_to_capture is greater than amount capture_amount: {amount_to_capture:?} request_amount: {amount:?}"
                        )
                        }))
                    })
                }
                api::Amount::Zero => {
                    // If the amount is Null but still amount_to_capture is passed this is invalid and
                    Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                        message: "amount_to_capture should not exist for when amount = 0"
                            .to_string()
                    }))
                }
            }
        }
    }
}

#[cfg(feature = "v1")]
/// if capture method = automatic, amount_to_capture(if provided) must be equal to amount
#[instrument(skip_all)]
pub fn validate_amount_to_capture_and_capture_method(
    payment_attempt: Option<&PaymentAttempt>,
    request: &api_models::payments::PaymentsRequest,
) -> CustomResult<(), errors::ApiErrorResponse> {
    let option_net_amount = hyperswitch_domain_models::payments::payment_attempt::NetAmount::from_payments_request_and_payment_attempt(
        request,
        payment_attempt,
    );
    let capture_method = request
        .capture_method
        .or(payment_attempt
            .map(|payment_attempt| payment_attempt.capture_method.unwrap_or_default()))
        .unwrap_or_default();
    if matches!(
        capture_method,
        api_enums::CaptureMethod::Automatic | api_enums::CaptureMethod::SequentialAutomatic
    ) {
        let total_capturable_amount =
            option_net_amount.map(|net_amount| net_amount.get_total_amount());

        let amount_to_capture = request
            .amount_to_capture
            .or(payment_attempt.and_then(|pa| pa.amount_to_capture));

        if let Some((total_capturable_amount, amount_to_capture)) =
            total_capturable_amount.zip(amount_to_capture)
        {
            utils::when(amount_to_capture != total_capturable_amount, || {
                Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                    message: "amount_to_capture must be equal to total_capturable_amount when capture_method = automatic".into()
                }))
            })
        } else {
            Ok(())
        }
    } else {
        Ok(())
    }
}

#[instrument(skip_all)]
pub fn validate_card_data(
    payment_method_data: Option<api::PaymentMethodData>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    if let Some(api::PaymentMethodData::Card(card)) = payment_method_data {
        let cvc = card.card_cvc.peek().to_string();
        if cvc.len() < 3 || cvc.len() > 4 {
            Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                message: "Invalid card_cvc length".to_string()
            }))?
        }
        let card_cvc =
            cvc.parse::<u16>()
                .change_context(errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "card_cvc",
                })?;
        ::cards::CardSecurityCode::try_from(card_cvc).change_context(
            errors::ApiErrorResponse::PreconditionFailed {
                message: "Invalid Card CVC".to_string(),
            },
        )?;

        validate_card_expiry(&card.card_exp_month, &card.card_exp_year)?;
    }
    Ok(())
}

#[instrument(skip_all)]
pub fn validate_card_expiry(
    card_exp_month: &masking::Secret<String>,
    card_exp_year: &masking::Secret<String>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    let exp_month = card_exp_month
        .peek()
        .to_string()
        .parse::<u8>()
        .change_context(errors::ApiErrorResponse::InvalidDataValue {
            field_name: "card_exp_month",
        })?;
    let month = ::cards::CardExpirationMonth::try_from(exp_month).change_context(
        errors::ApiErrorResponse::PreconditionFailed {
            message: "Invalid Expiry Month".to_string(),
        },
    )?;

    let mut year_str = card_exp_year.peek().to_string();
    if year_str.len() == 2 {
        year_str = format!("20{year_str}");
    }
    let exp_year =
        year_str
            .parse::<u16>()
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: "card_exp_year",
            })?;
    let year = ::cards::CardExpirationYear::try_from(exp_year).change_context(
        errors::ApiErrorResponse::PreconditionFailed {
            message: "Invalid Expiry Year".to_string(),
        },
    )?;

    let card_expiration = ::cards::CardExpiration { month, year };
    let is_expired = card_expiration.is_expired().change_context(
        errors::ApiErrorResponse::PreconditionFailed {
            message: "Invalid card data".to_string(),
        },
    )?;
    if is_expired {
        Err(report!(errors::ApiErrorResponse::PreconditionFailed {
            message: "Card Expired".to_string()
        }))?
    }

    Ok(())
}

pub fn infer_payment_type(
    amount: api::Amount,
    mandate_type: Option<&api::MandateTransactionType>,
) -> api_enums::PaymentType {
    match mandate_type {
        Some(api::MandateTransactionType::NewMandateTransaction) => {
            if let api::Amount::Value(_) = amount {
                api_enums::PaymentType::NewMandate
            } else {
                api_enums::PaymentType::SetupMandate
            }
        }

        Some(api::MandateTransactionType::RecurringMandateTransaction) => {
            api_enums::PaymentType::RecurringMandate
        }

        None => api_enums::PaymentType::Normal,
    }
}

pub fn validate_mandate(
    req: impl Into<api::MandateValidationFields>,
    is_confirm_operation: bool,
) -> CustomResult<Option<api::MandateTransactionType>, errors::ApiErrorResponse> {
    let req: api::MandateValidationFields = req.into();
    match req.validate_and_get_mandate_type().change_context(
        errors::ApiErrorResponse::MandateValidationFailed {
            reason: "Expected one out of recurring_details and mandate_data but got both".into(),
        },
    )? {
        Some(api::MandateTransactionType::NewMandateTransaction) => {
            validate_new_mandate_request(req, is_confirm_operation)?;
            Ok(Some(api::MandateTransactionType::NewMandateTransaction))
        }
        Some(api::MandateTransactionType::RecurringMandateTransaction) => {
            validate_recurring_mandate(req)?;
            Ok(Some(
                api::MandateTransactionType::RecurringMandateTransaction,
            ))
        }
        None => Ok(None),
    }
}

pub fn validate_recurring_details_and_token(
    recurring_details: &Option<RecurringDetails>,
    payment_token: &Option<String>,
    mandate_id: &Option<String>,
) -> CustomResult<(), errors::ApiErrorResponse> {
    utils::when(


// Implementation: impl Deref for for FloatDecimal
// File: crates/router/src/utils/currency.rs
// Module: router
// Methods: 1 total (0 public)

impl Deref for for FloatDecimal


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

pub mod transformers;
use api_models::{self, webhooks::IncomingWebhookEvent};
#[cfg(feature = "payouts")]
use base64::Engine;
#[cfg(feature = "payouts")]
use common_utils::crypto;
use common_utils::errors::CustomResult;
#[cfg(feature = "payouts")]
use common_utils::ext_traits::{ByteSliceExt as _, BytesExt};
#[cfg(feature = "payouts")]
use common_utils::request::RequestContent;
#[cfg(feature = "payouts")]
use common_utils::request::{Method, Request, RequestBuilder};
#[cfg(feature = "payouts")]
use common_utils::types::MinorUnitForConnector;
#[cfg(feature = "payouts")]
use common_utils::types::{AmountConvertor, MinorUnit};
#[cfg(not(feature = "payouts"))]
use error_stack::report;
use error_stack::ResultExt;
#[cfg(feature = "payouts")]
use http::HeaderName;
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::router_data::{ErrorResponse, RouterData};
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::router_flow_types::PoFulfill;
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::types::{PayoutsData, PayoutsResponseData, PayoutsRouterData};
use hyperswitch_domain_models::{
    api::ApplicationResponse,
    router_data::{AccessToken, ConnectorAuthType},
    router_flow_types::{
        AccessTokenAuth, Authorize, Capture, Execute, PSync, PaymentMethodToken, RSync, Session,
        SetupMandate, Void,
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentsResponseData, RefundsResponseData, SupportedPaymentMethods,
    },
};
#[cfg(feature = "payouts")]
use hyperswitch_interfaces::events::connector_api_logs::ConnectorEvent;
#[cfg(feature = "payouts")]
use hyperswitch_interfaces::types::{PayoutFulfillType, Response};
use hyperswitch_interfaces::{
    api::{self, ConnectorCommon, ConnectorIntegration, ConnectorSpecifications},
    configs::Connectors,
    errors::ConnectorError,
    webhooks::{IncomingWebhook, IncomingWebhookFlowError, IncomingWebhookRequestDetails},
};
use masking::{Mask as _, Maskable, Secret};
#[cfg(feature = "payouts")]
use ring::hmac;
#[cfg(feature = "payouts")]
use router_env::{instrument, tracing};
#[cfg(feature = "payouts")]
use transformers::get_adyen_webhook_event;

use self::transformers as adyenplatform;
use crate::constants::headers;
#[cfg(feature = "payouts")]
use crate::types::ResponseRouterData;
#[cfg(feature = "payouts")]
use crate::utils::convert_amount;

#[derive(Clone)]
pub struct Adyenplatform {
    #[cfg(feature = "payouts")]
    amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
}
impl Adyenplatform {
    pub const fn new() -> &'static Self {
        &Self {
            #[cfg(feature = "payouts")]
            amount_converter: &MinorUnitForConnector,
        }
    }
}

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

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

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

    #[cfg(feature = "payouts")]
    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        let response: adyenplatform::AdyenTransferErrorResponse = res
            .response
            .parse_struct("AdyenTransferErrorResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        let message = if let Some(invalid_fields) = &response.invalid_fields {
            match serde_json::to_string(invalid_fields) {
                Ok(invalid_fields_json) => format!(
                    "{}\nInvalid fields: {}",
                    response.title, invalid_fields_json
                ),
                Err(_) => response.title.clone(),
            }
        } else if let Some(detail) = &response.detail {
            format!("{}\nDetail: {}", response.title, detail)
        } else {
            response.title.clone()
        };

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

impl api::Payment for Adyenplatform {}
impl api::PaymentAuthorize for Adyenplatform {}
impl api::PaymentSync for Adyenplatform {}
impl api::PaymentVoid for Adyenplatform {}
impl api::PaymentCapture for Adyenplatform {}
impl api::MandateSetup for Adyenplatform {}
impl api::ConnectorAccessToken for Adyenplatform {}
impl api::PaymentToken for Adyenplatform {}
impl api::ConnectorValidation for Adyenplatform {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Adyenplatform
{
}

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

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

impl api::PaymentSession for Adyenplatform {}

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

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Adyenplatform {}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Adyenplatform {}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData>
    for Adyenplatform
{
}

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

impl api::Payouts for Adyenplatform {}
#[cfg(feature = "payouts")]
impl api::PayoutFulfill for Adyenplatform {}

#[cfg(feature = "payouts")]
impl ConnectorIntegration<PoFulfill, PayoutsData, PayoutsResponseData> for Adyenplatform {
    fn get_url(
        &self,
        _req: &PayoutsRouterData<PoFulfill>,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!(
            "{}btl/v4/transfers",
            connectors.adyenplatform.base_url,
        ))
    }

    fn get_headers(
        &self,
        req: &PayoutsRouterData<PoFulfill>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PayoutFulfillType::get_content_type(self).to_string().into(),
        )];
        let auth = adyenplatform::AdyenplatformAuthType::try_from(&req.connector_auth_type)
            .change_context(ConnectorError::FailedToObtainAuthType)?;
        let mut api_key = vec![(
            headers::AUTHORIZATION.to_string(),
            auth.api_key.into_masked(),
        )];
        header.append(&mut api_key);
        Ok(header)
    }

    fn get_request_body(
        &self,
        req: &PayoutsRouterData<PoFulfill>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.destination_currency,
        )?;
        let connector_router_data =
            adyenplatform::AdyenPlatformRouterData::try_from((amount, req))?;
        let connector_req = adyenplatform::AdyenTransferRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PayoutsRouterData<PoFulfill>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        let request = RequestBuilder::new()
            .method(Method::Post)
            .url(&PayoutFulfillType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(PayoutFulfillType::get_headers(self, req, connectors)?)
            .set_body(PayoutFulfillType::get_request_body(self, req, connectors)?)
            .build();

        Ok(Some(request))
    }

    #[instrument(skip_all)]
    fn handle_response(
        &self,
        data: &PayoutsRouterData<PoFulfill>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PayoutsRouterData<PoFulfill>, ConnectorError> {
        let response: adyenplatform::AdyenTransferResponse = res
            .response
            .parse_struct("AdyenTransferResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

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

impl api::Refund for Adyenplatform {}
impl api::RefundExecute for Adyenplatform {}
impl api::RefundSync for Adyenplatform {}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Adyenplatform {}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Adyenplatform {}

#[async_trait::async_trait]
impl IncomingWebhook for Adyenplatform {
    #[cfg(feature = "payouts")]
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, ConnectorError> {
        Ok(Box::new(crypto::HmacSha256))
    }

    #[cfg(feature = "payouts")]
    fn get_webhook_source_verification_signature(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        let base64_signature = request
            .headers
            .get(HeaderName::from_static("hmacsignature"))
            .ok_or(ConnectorError::WebhookSourceVerificationFailed)?;
        Ok(base64_signature.as_bytes().to_vec())
    }

    #[cfg(feature = "payouts")]
    fn get_webhook_source_verification_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        Ok(request.body.to_vec())
    }

    #[cfg(feature = "payouts")]
    async fn verify_webhook_source(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>,
        connector_label: &str,
    ) -> CustomResult<bool, ConnectorError> {
        use common_utils::consts;

        let connector_webhook_secrets = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_label,
                connector_webhook_details,
            )
            .await
            .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

        let signature = self
            .get_webhook_source_verification_signature(request, &connector_webhook_secrets)
            .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

        let message = self
            .get_webhook_source_verification_message(
                request,
                merchant_id,
                &connector_webhook_secrets,
            )
            .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

        let raw_key = hex::decode(connector_webhook_secrets.secret)
            .change_context(ConnectorError::WebhookVerificationSecretInvalid)?;

        let signing_key = hmac::Key::new(hmac::HMAC_SHA256, &raw_key);
        let signed_messaged = hmac::sign(&signing_key, &message);
        let payload_sign = consts::BASE64_ENGINE.encode(signed_messaged.as_ref());
        Ok(payload_sign.as_bytes().eq(&signature))
    }

    fn get_webhook_object_reference_id(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            let webhook_body: adyenplatform::AdyenplatformIncomingWebhook = request
                .body
                .parse_struct("AdyenplatformIncomingWebhook")
                .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

            Ok(api_models::webhooks::ObjectReferenceId::PayoutId(
                api_models::webhooks::PayoutIdType::PayoutAttemptId(webhook_body.data.reference),
            ))
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }

    fn get_webhook_api_response(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
        error_kind: Option<IncomingWebhookFlowError>,
    ) -> CustomResult<ApplicationResponse<serde_json::Value>, ConnectorError> {
        if error_kind.is_some() {
            Ok(ApplicationResponse::JsonWithHeaders((
                serde_json::Value::Null,
                vec![(
                    "x-http-code".to_string(),
                    Maskable::Masked(Secret::new("404".to_string())),
                )],
            )))
        } else {
            Ok(ApplicationResponse::StatusOk)
        }
    }

    fn get_webhook_event_type(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            let webhook_body: adyenplatform::AdyenplatformIncomingWebhook = request
                .body
                .parse_struct("AdyenplatformIncomingWebhook")
                .change_context(ConnectorError::WebhookSourceVerificationFailed)?;

            Ok(get_adyen_webhook_event(
                webhook_body.webhook_type,
                webhook_body.data.status,
                webhook_body.data.tracking,
                webhook_body.data.category.as_ref(),
            ))
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }

    fn get_webhook_resource_object(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            let webhook_body: adyenplatform::AdyenplatformIncomingWebhook = request
                .body
                .parse_struct("AdyenplatformIncomingWebhook")
                .change_context(ConnectorError::WebhookSourceVerificationFailed)?;
            Ok(Box::new(webhook_body))
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }
}

static ADYENPLATFORM_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Adyen Platform",
    description: "Adyen Platform for marketplace payouts and disbursements",
    connector_type: common_enums::HyperswitchConnectorCategory::PayoutProcessor,
    integration_status: common_enums::ConnectorIntegrationStatus::Sandbox,
};

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

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        None
    }

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


// Struct: ErrorDistributionRow
// File: crates/analytics/src/payment_intents/accumulator.rs
// Module: analytics
// Implementations: 0

pub struct ErrorDistributionRow


// Implementation: impl api::PaymentSync for for Nexinets
// File: crates/hyperswitch_connectors/src/connectors/nexinets.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Nexinets


    };
    let payment_response_data = PaymentsResponseData::TransactionResponse {
        resource_id: ResponseId::ConnectorTransactionId(response.id.clone()),
        redirection_data: Box::new(redirection_data),
        mandate_reference: Box::new(None),
        connector_metadata: None,
        network_txn_id: None,
        connector_response_reference_id: None,
        incremental_authorization_allowed: None,
        charges: None,
    };
    Ok((status, error, payment_response_data))
}

impl<F, T> TryFrom<ResponseRouterData<F, ApiResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        value: ResponseRouterData<F, ApiResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let (status, error, payment_response_data) =
            get_zen_response(value.response.clone(), value.http_code)?;

        Ok(Self {
            status,
            response: error.map_or_else(|| Ok(payment_response_data), Err),
            ..value.data
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, CheckoutResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        value: ResponseRouterData<F, CheckoutResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let redirection_data = Some(RedirectForm::from((
            value.response.redirect_url,
            Method::Get,
        )));
        Ok(Self {
            status: enums::AttemptStatus::AuthenticationPending,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::NoResponseId,
                redirection_data: Box::new(redirection_data),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..value.data
        })
    }
}

#[derive(Default, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenRefundRequest {
    amount: String,
    transaction_id: String,
    currency: enums::Currency,
    merchant_transaction_id: String,
}

impl<F> TryFrom<&ZenRouterData<&types::RefundsRouterData<F>>> for ZenRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &ZenRouterData<&types::RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: item.amount.to_owned(),
            transaction_id: item.router_data.request.connector_transaction_id.clone(),
            currency: item.router_data.request.currency,
            merchant_transaction_id: item.router_data.request.refund_id.clone(),
        })
    }
}

#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum RefundStatus {
    Authorized,
    Accepted,
    #[default]
    Pending,
    Rejected,
}

impl From<RefundStatus> for enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Accepted => Self::Success,
            RefundStatus::Pending | RefundStatus::Authorized => Self::Pending,
            RefundStatus::Rejected => Self::Failure,
        }
    }
}

#[derive(Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RefundResponse {
    id: String,
    status: RefundStatus,
    reject_code: Option<String>,
    reject_reason: Option<String>,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>>
    for types::RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        let (error, refund_response_data) = get_zen_refund_response(item.response, item.http_code)?;
        Ok(Self {
            response: error.map_or_else(|| Ok(refund_response_data), Err),
            ..item.data
        })
    }
}

fn get_zen_refund_response(
    response: RefundResponse,
    status_code: u16,
) -> CustomResult<(Option<ErrorResponse>, RefundsResponseData), errors::ConnectorError> {
    let refund_status = enums::RefundStatus::from(response.status);
    let error = if utils::is_refund_failure(refund_status) {
        Some(ErrorResponse {
            code: response
                .reject_code
                .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
            message: response
                .reject_reason
                .clone()
                .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
            reason: response.reject_reason,
            status_code,
            attempt_status: None,
            connector_transaction_id: Some(response.id.clone()),
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    } else {
        None
    };
    let refund_response_data = RefundsResponseData {
        connector_refund_id: response.id,
        refund_status,
    };
    Ok((error, refund_response_data))
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for types::RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::from(item.response.status);
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id,
                refund_status,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenWebhookBody {
    #[serde(rename = "transactionId")]
    pub id: String,
    pub merchant_transaction_id: String,
    pub amount: String,
    pub currency: String,
    pub status: ZenPaymentStatus,
}

#[derive(Debug, Clone, Deserialize)]
pub struct ZenWebhookSignature {
    pub hash: String,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenWebhookObjectReference {
    #[serde(rename = "type")]
    pub transaction_type: ZenWebhookTxnType,
    pub transaction_id: String,
    pub merchant_transaction_id: String,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenWebhookEventType {
    #[serde(rename = "type")]
    pub transaction_type: ZenWebhookTxnType,
    pub transaction_id: String,
    pub status: ZenPaymentStatus,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ZenWebhookTxnType {
    TrtPurchase,
    TrtRefund,
    #[serde(other)]
    Unknown,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct ZenErrorResponse {
    pub error: Option<ZenErrorBody>,
    pub message: Option<String>,
}

#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct ZenErrorBody {
    pub message: String,
    pub code: String,
}


// Struct: PmTokenUpdated
// File: crates/router/src/core/payment_methods/tokenize/payment_method_executor.rs
// Module: router
// Implementations: 1
// Traits: State

pub struct PmTokenUpdated


// Struct: DeliverySpecifications
// File: crates/hyperswitch_connectors/src/connectors/amazonpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DeliverySpecifications


// Implementation: impl PayoutFulfill for for Payone
// File: crates/hyperswitch_connectors/src/connectors/payone.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl PayoutFulfill for for Payone


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

impl api::RefundSync for for Payu


// Struct: RefundLink
// File: crates/hyperswitch_connectors/src/connectors/gocardless/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RefundLink


// Function: set_shipping_data
// File: crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs
// Module: hyperswitch_connectors

pub fn set_shipping_data(mut self, address: Option<&Address>) -> Result<Self, Error>


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

pub fn with_mandate_flow(
        mut self,
        is_mandate_flow: bool,
        connector_mandate_details: Option<
            hyperswitch_domain_models::mandates::PaymentsMandateReference,
        >,
    ) -> Self


// Function: check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata
// File: crates/router/src/utils.rs
// Module: router

pub fn check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata(
    metadata: Option<Value>,
) -> bool


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

pub struct SamsungPayTokenData


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

pub fn generate_payment_method_session_response(
    payment_method_session: hyperswitch_domain_models::payment_methods::PaymentMethodSession,
    client_secret: Secret<String>,
    associated_payment: Option<api_models::payments::PaymentsResponse>,
    tokenization_service_response: Option<api_models::tokenization::GenericTokenizationResponse>,
) -> api_models::payment_methods::PaymentMethodSessionResponse


// Struct: BlocklistLookup
// File: crates/diesel_models/src/blocklist_lookup.rs
// Module: diesel_models
// Implementations: 0

pub struct BlocklistLookup


// Implementation: impl GetPaymentMethodType for for GiftCardData
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Methods: 1 total (0 public)

impl GetPaymentMethodType for for GiftCardData


// Function: rule_conditions_array
// File: crates/euclid/src/frontend/ast/parser.rs
// Module: euclid

pub fn rule_conditions_array(input: &str) -> ParseResult<&str, Vec<ast::IfStatement>>


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

pub struct NmiWebhookObjectReference


// Struct: OnboardTransferMethodRequest
// File: crates/hyperswitch_connectors/src/connectors/nomupay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct OnboardTransferMethodRequest


// Implementation: impl SubscriptionNew
// File: crates/diesel_models/src/subscription.rs
// Module: diesel_models
// Methods: 2 total (2 public)

impl SubscriptionNew


// Struct: RoutingDictionary
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 0

pub struct RoutingDictionary


// Function: delete_merchant_account
// File: crates/router/src/routes/admin.rs
// Module: router

pub fn delete_merchant_account(
    state: web::Data<AppState>,
    req: HttpRequest,
    mid: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse


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

impl api::PaymentCapture for for Payeezy


// Function: is_update_required
// File: crates/router/src/types/payment_methods.rs
// Module: router

pub fn is_update_required(
        &self,
        data_stored_in_vault: api::payment_methods::CardDetailFromLocker,
    ) -> bool


// Struct: DBEnumWrapper
// File: crates/analytics/src/types.rs
// Module: analytics
// Implementations: 2
// Traits: Default, FromStr

pub struct DBEnumWrapper<T: FromStr + Display>


// Implementation: impl AuthenticationId
// File: crates/common_utils/src/id_type/authentication.rs
// Module: common_utils
// Methods: 2 total (2 public)

impl AuthenticationId


// Function: create_access_token
// File: crates/router/src/core/payouts/access_token.rs
// Module: router

pub fn create_access_token<F: Clone + 'static>(
    state: &SessionState,
    connector_data: &api_types::ConnectorData,
    merchant_context: &domain::MerchantContext,
    router_data: &mut types::PayoutsRouterData<F>,
    payout_type: Option<enums::PayoutType>,
) -> RouterResult<()>


// Function: profile_acquirer_create
// File: crates/openapi/src/routes/profile_acquirer.rs
// Module: openapi

pub fn profile_acquirer_create()


// Struct: XenditRouterData
// File: crates/hyperswitch_connectors/src/connectors/xendit/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct XenditRouterData<T>


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

pub fn parse_csv(
    merchant_id: &id_type::MerchantId,
    data: &[u8],
) -> csv::Result<Vec<payment_methods_api::CardNetworkTokenizeRequest>>


// Implementation: impl api::PaymentSync for for Phonepe
// File: crates/hyperswitch_connectors/src/connectors/phonepe.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Phonepe


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

pub struct TsysPaymentAuthSaleRequest


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

impl api::PaymentCapture for for Opayo


// Struct: RedirectUrls
// File: crates/hyperswitch_connectors/src/connectors/iatapay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RedirectUrls


// Struct: PaysafeApplePayDecryptedData
// File: crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaysafeApplePayDecryptedData


// Function: insert
// File: crates/diesel_models/src/query/gsm.rs
// Module: diesel_models

pub fn insert(self, conn: &PgPooledConn) -> StorageResult<GatewayStatusMap>


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

impl ConnectorSpecifications for for Bitpay


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

pub struct BokuRefundRequest


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

impl api::RefundSync for for Paypal


// Function: list_blocked_payment_methods
// File: crates/router/src/routes/blocklist.rs
// Module: router

pub fn list_blocked_payment_methods(
    state: web::Data<AppState>,
    req: HttpRequest,
    query_payload: web::Query<api_blocklist::ListBlocklistQuery>,
) -> HttpResponse


// Implementation: impl api::PaymentSync for for Noon
// File: crates/hyperswitch_connectors/src/connectors/noon.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Noon


// Struct: SetupMandateRequestData
// File: crates/hyperswitch_domain_models/src/router_request_types.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct SetupMandateRequestData


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

/// Header Constants
pub(crate) mod headers {
    pub(crate) const ACCEPT: &str = "Accept";
    pub(crate) const API_KEY: &str = "API-KEY";
    pub(crate) const APIKEY: &str = "apikey";
    pub(crate) const API_TOKEN: &str = "Api-Token";
    pub(crate) const AUTHORIZATION: &str = "Authorization";
    pub(crate) const CONTENT_TYPE: &str = "Content-Type";
    pub(crate) const DATE: &str = "Date";
    pub(crate) const IDEMPOTENCY_KEY: &str = "Idempotency-Key";
    pub(crate) const MESSAGE_SIGNATURE: &str = "Message-Signature";
    pub(crate) const MERCHANT_ID: &str = "Merchant-ID";
    pub(crate) const MERCHANTID: &str = "MerchantID";
    pub(crate) const MERCHANT_TOKEN: &str = "MerchantToken";
    pub(crate) const REQUEST_ID: &str = "request-id";
    pub(crate) const NONCE: &str = "nonce";
    pub(crate) const TIMESTAMP: &str = "Timestamp";
    pub(crate) const TOKEN: &str = "token";
    pub(crate) const X_ACCEPT_VERSION: &str = "X-Accept-Version";
    pub(crate) const X_CC_API_KEY: &str = "X-CC-Api-Key";
    pub(crate) const X_CC_VERSION: &str = "X-CC-Version";
    pub(crate) const X_DATE: &str = "X-Date";
    pub(crate) const X_LOGIN: &str = "X-Login";
    pub(crate) const X_NN_ACCESS_KEY: &str = "X-NN-Access-Key";
    pub(crate) const X_TRANS_KEY: &str = "X-Trans-Key";
    pub(crate) const X_RANDOM_VALUE: &str = "X-RandomValue";
    pub(crate) const X_REQUEST_DATE: &str = "X-RequestDate";
    pub(crate) const X_VERSION: &str = "X-Version";
    pub(crate) const X_API_KEY: &str = "X-Api-Key";
    pub(crate) const CORRELATION_ID: &str = "Correlation-Id";
    pub(crate) const WP_API_VERSION: &str = "WP-Api-Version";
    pub(crate) const STRIPE_COMPATIBLE_CONNECT_ACCOUNT: &str = "Stripe-Account";
    pub(crate) const SOURCE: &str = "Source";
    pub(crate) const USER_AGENT: &str = "User-Agent";
    pub(crate) const KEY: &str = "key";
    pub(crate) const X_SIGNATURE: &str = "X-Signature";
    pub(crate) const SOAP_ACTION: &str = "SOAPAction";
    pub(crate) const X_PROFILE_ID: &str = "X-Profile-Id";
}

/// Unsupported response type error message
pub const UNSUPPORTED_ERROR_MESSAGE: &str = "Unsupported response type";

/// Error message for Authentication Error from the connector
pub const CONNECTOR_UNAUTHORIZED_ERROR: &str = "Authentication Error from the connector";

/// Error message when Refund request has been voided.
pub const REFUND_VOIDED: &str = "Refund request has been voided.";

pub const LOW_BALANCE_ERROR_MESSAGE: &str = "Insufficient balance in the payment method";

pub const DUIT_NOW_BRAND_COLOR: &str = "#ED2E67";

pub const DUIT_NOW_BRAND_TEXT: &str = "MALAYSIA NATIONAL QR";

pub(crate) const CANNOT_CONTINUE_AUTH: &str =
    "Cannot continue with Authorization due to failed Liability Shift.";

#[cfg(feature = "payouts")]
pub(crate) const DEFAULT_NOTIFICATION_SCRIPT_LANGUAGE: &str = "en-US";

pub(crate) const PLAN_ITEM_TYPE: &str = "plan";


// Function: get_metrics
// File: crates/analytics/src/payments/core.rs
// Module: analytics

pub fn get_metrics(
    pool: &AnalyticsProvider,
    ex_rates: &Option<ExchangeRates>,
    auth: &AuthInfo,
    req: GetPaymentMetricRequest,
) -> AnalyticsResult<PaymentsMetricsResponse<MetricsBucketResponse>>


// Struct: FrmConfigsObject
// File: crates/router/src/core/fraud_check/types.rs
// Module: router
// Implementations: 0

pub struct FrmConfigsObject


// Struct: CalSuccessRateEventRequest
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 0

pub struct CalSuccessRateEventRequest


// Function: server
// File: crates/router/src/routes/app.rs
// Module: router

pub fn server(state: AppState) -> Scope


// Function: generic_update_by_id
// File: crates/diesel_models/src/query/generics.rs
// Module: diesel_models

pub fn generic_update_by_id<T, V, Pk, R>(
    conn: &PgPooledConn,
    id: Pk,
    values: V,
) -> StorageResult<R>
where
    T: FindDsl<Pk> + HasTable<Table = T> + LimitDsl + Table + 'static,
    V: AsChangeset<Target = <Find<T, Pk> as HasTable>::Table> + Debug,
    Find<T, Pk>: IntoUpdateTarget + QueryFragment<Pg> + RunQueryDsl<PgConnection> + Send + 'static,
    UpdateStatement<
        <Find<T, Pk> as HasTable>::Table,
        <Find<T, Pk> as IntoUpdateTarget>::WhereClause,
        <V as AsChangeset>::Changeset,
    >: AsQuery + LoadQuery<'static, PgConnection, R> + QueryFragment<Pg> + Send + 'static,
    Find<T, Pk>: LimitDsl,
    Limit<Find<T, Pk>>: LoadQuery<'static, PgConnection, R>,
    R: Send + 'static,
    Pk: Clone + Debug,

    // For cloning query (UpdateStatement)
    <Find<T, Pk> as HasTable>::Table: Clone,
    <Find<T, Pk> as IntoUpdateTarget>::WhereClause: Clone,
    <V as AsChangeset>::Changeset: Clone,
    <<Find<T, Pk> as HasTable>::Table as QuerySource>::FromClause: Clone,


// Implementation: impl Stripebilling
// File: crates/hyperswitch_connectors/src/connectors/stripebilling.rs
// Module: hyperswitch_connectors
// Methods: 1 total (1 public)

impl Stripebilling