// Struct: FrmFilterValue
// File: crates/api_models/src/analytics.rs
// Module: api_models
// Implementations: 1
// Traits: ApiEventMetric

pub struct FrmFilterValue


// Struct: ElavonRefundRequest
// File: crates/hyperswitch_connectors/src/connectors/elavon/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ElavonRefundRequest


// Function: retrieve_poll_status
// File: crates/router/src/core/poll.rs
// Module: router

pub fn retrieve_poll_status(
    state: SessionState,
    req: crate::types::api::PollId,
    merchant_context: domain::MerchantContext,
) -> RouterResponse<PollResponse>


// Function: four_digits
// File: crates/cards/src/lib.rs
// Module: cards

pub fn four_digits(&self) -> String


// Implementation: impl api::PaymentSession for for Globalpay
// File: crates/hyperswitch_connectors/src/connectors/globalpay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSession for for Globalpay


// Function: customers_retrieve
// File: crates/router/src/routes/customers.rs
// Module: router

pub fn customers_retrieve(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<id_type::CustomerId>,
) -> HttpResponse


// Struct: SantanderTime
// File: crates/hyperswitch_connectors/src/connectors/santander/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SantanderTime


// Implementation: impl UploadFile for for Worldpayvantiv
// File: crates/hyperswitch_connectors/src/connectors/worldpayvantiv.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl UploadFile for for Worldpayvantiv


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

pub struct XenditRefundRequest


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

impl api::PaymentAuthorize for for Wellsfargopayout


// Implementation: impl RedisSettings
// File: crates/redis_interface/src/types.rs
// Module: redis_interface
// Methods: 1 total (1 public)

impl RedisSettings


// Function: parse_routing_algorithm
// File: crates/api_models/src/routing.rs
// Module: api_models

pub fn parse_routing_algorithm(
        value: Option<pii::SecretSerdeValue>,
    ) -> Result<Option<Self>, error_stack::Report<ParsingError>>


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

impl Responder


// Struct: CoingateConnectorMetadataObject
// File: crates/hyperswitch_connectors/src/connectors/coingate/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CoingateConnectorMetadataObject


// Struct: DeutschebankThreeDSInitializeResponseChallengeRequired
// File: crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DeutschebankThreeDSInitializeResponseChallengeRequired


// Function: get_metadata_as_secret
// File: crates/api_models/src/admin.rs
// Module: api_models

pub fn get_metadata_as_secret(
        &self,
    ) -> CustomResult<Option<pii::SecretSerdeValue>, errors::ParsingError>


// Struct: OnboardingSyncRequest
// File: crates/api_models/src/connector_onboarding.rs
// Module: api_models
// Implementations: 0

pub struct OnboardingSyncRequest


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

impl api::PaymentAuthorize for for Mpgs


// File: crates/storage_impl/src/redis.rs
// Module: storage_impl
// Public functions: 2
// Public structs: 1

pub mod cache;
pub mod kv_store;
pub mod pub_sub;

use std::sync::{atomic, Arc};

use router_env::tracing::Instrument;

use self::{kv_store::RedisConnInterface, pub_sub::PubSubInterface};

#[derive(Clone)]
pub struct RedisStore {
    // Maybe expose the redis_conn via traits instead of the making the field public
    pub(crate) redis_conn: Arc<redis_interface::RedisConnectionPool>,
}

impl std::fmt::Debug for RedisStore {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("CacheStore")
            .field("redis_conn", &"Redis conn doesn't implement debug")
            .finish()
    }
}

impl RedisStore {
    pub async fn new(
        conf: &redis_interface::RedisSettings,
    ) -> error_stack::Result<Self, redis_interface::errors::RedisError> {
        Ok(Self {
            redis_conn: Arc::new(redis_interface::RedisConnectionPool::new(conf).await?),
        })
    }

    pub fn set_error_callback(&self, callback: tokio::sync::oneshot::Sender<()>) {
        let redis_clone = self.redis_conn.clone();
        let _task_handle = tokio::spawn(
            async move {
                redis_clone.on_error(callback).await;
            }
            .in_current_span(),
        );
    }
}

impl RedisConnInterface for RedisStore {
    fn get_redis_conn(
        &self,
    ) -> error_stack::Result<
        Arc<redis_interface::RedisConnectionPool>,
        redis_interface::errors::RedisError,
    > {
        if self
            .redis_conn
            .is_redis_available
            .load(atomic::Ordering::SeqCst)
        {
            Ok(self.redis_conn.clone())
        } else {
            Err(redis_interface::errors::RedisError::RedisConnectionError.into())
        }
    }
}


// Function: execute_payment
// File: crates/router/src/core/revenue_recovery/types.rs
// Module: router

pub fn execute_payment(
        state: &SessionState,
        _merchant_id: &id_type::MerchantId,
        payment_intent: &PaymentIntent,
        process: &storage::ProcessTracker,
        profile: &domain::Profile,
        merchant_context: domain::MerchantContext,
        revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
        revenue_recovery_metadata: &PaymentRevenueRecoveryMetadata,
        latest_attempt_id: &id_type::GlobalAttemptId,
    ) -> RecoveryResult<Self>


// Struct: ConsumerSettings
// File: crates/scheduler/src/settings.rs
// Module: scheduler
// Implementations: 1
// Traits: Default

pub struct ConsumerSettings


// Struct: AppState
// File: crates/router/src/routes/app.rs
// Module: router
// Implementations: 3
// Traits: scheduler::SchedulerAppState, AppStateInfo

pub struct AppState


// Function: confirm_subscription
// File: crates/router/src/core/subscription.rs
// Module: router

pub fn confirm_subscription(
    state: SessionState,
    merchant_context: MerchantContext,
    profile_id: String,
    request: subscription_types::ConfirmSubscriptionRequest,
    subscription_id: common_utils::id_type::SubscriptionId,
) -> RouterResponse<subscription_types::ConfirmSubscriptionResponse>


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

pub fn create_payment_method_core(
    state: &SessionState,
    _request_state: &routes::app::ReqState,
    req: api::PaymentMethodCreate,
    merchant_context: &domain::MerchantContext,
    profile: &domain::Profile,
) -> RouterResult<(api::PaymentMethodResponse, domain::PaymentMethod)>


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

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


// Implementation: impl StripeApis
// File: crates/router/src/compatibility/stripe.rs
// Module: router
// Methods: 1 total (1 public)

impl StripeApis


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

pub struct NmiActionBody


// Function: get_dispute_evidence_block
// File: crates/router/src/core/disputes/transformers.rs
// Module: router

pub fn get_dispute_evidence_block(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    evidence_type: EvidenceType,
    file_id: String,
) -> CustomResult<api_models::disputes::DisputeEvidenceBlock, errors::ApiErrorResponse>


// Function: custom_revenue_recovery_core
// File: crates/router/src/core/revenue_recovery/api.rs
// Module: router

pub fn custom_revenue_recovery_core(
    state: SessionState,
    req_state: ReqState,
    merchant_context: MerchantContext,
    profile: domain::Profile,
    request: api_models::payments::RecoveryPaymentsCreate,
) -> RouterResponse<payments_api::RecoveryPaymentsResponse>


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

impl api::MandateSetup for for Stax


// Function: dummy_connector_payment_data
// File: crates/router/src/routes/dummy_connector.rs
// Module: router

pub fn dummy_connector_payment_data(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<String>,
) -> impl actix_web::Responder


// Implementation: impl EmailData for for ApiKeyExpiryReminder
// File: crates/router/src/services/email/types.rs
// Module: router
// Methods: 1 total (0 public)

impl EmailData for for ApiKeyExpiryReminder


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

pub fn payment_methods_session_update_payment_method(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
    request: payment_methods::PaymentMethodSessionUpdateSavedPaymentMethod,
) -> RouterResponse<payment_methods::PaymentMethodResponse>


// Function: new
// File: crates/hyperswitch_connectors/src/connectors/fiuu.rs
// Module: hyperswitch_connectors

pub fn new() -> &'static Self


// File: crates/diesel_models/src/enums.rs
// Module: diesel_models
// Public structs: 2

#[doc(hidden)]
pub mod diesel_exports {
    pub use super::{
        DbApiVersion as ApiVersion, DbAttemptStatus as AttemptStatus,
        DbAuthenticationType as AuthenticationType, DbBlocklistDataKind as BlocklistDataKind,
        DbCaptureMethod as CaptureMethod, DbCaptureStatus as CaptureStatus,
        DbCardDiscovery as CardDiscovery, DbConnectorStatus as ConnectorStatus,
        DbConnectorType as ConnectorType, DbCountryAlpha2 as CountryAlpha2, DbCurrency as Currency,
        DbDashboardMetadata as DashboardMetadata, DbDeleteStatus as DeleteStatus,
        DbDisputeStage as DisputeStage, DbDisputeStatus as DisputeStatus,
        DbEventClass as EventClass, DbEventObjectType as EventObjectType, DbEventType as EventType,
        DbFraudCheckStatus as FraudCheckStatus, DbFraudCheckType as FraudCheckType,
        DbFutureUsage as FutureUsage, DbGenericLinkType as GenericLinkType,
        DbIntentStatus as IntentStatus, DbMandateStatus as MandateStatus,
        DbMandateType as MandateType, DbMerchantStorageScheme as MerchantStorageScheme,
        DbOrderFulfillmentTimeOrigin as OrderFulfillmentTimeOrigin,
        DbPaymentMethodIssuerCode as PaymentMethodIssuerCode, DbPaymentSource as PaymentSource,
        DbPaymentType as PaymentType, DbPayoutStatus as PayoutStatus, DbPayoutType as PayoutType,
        DbProcessTrackerStatus as ProcessTrackerStatus, DbReconStatus as ReconStatus,
        DbRefundStatus as RefundStatus, DbRefundType as RefundType, DbRelayStatus as RelayStatus,
        DbRelayType as RelayType,
        DbRequestIncrementalAuthorization as RequestIncrementalAuthorization,
        DbRevenueRecoveryAlgorithmType as RevenueRecoveryAlgorithmType, DbRoleScope as RoleScope,
        DbRoutingAlgorithmKind as RoutingAlgorithmKind, DbRoutingApproach as RoutingApproach,
        DbScaExemptionType as ScaExemptionType,
        DbSuccessBasedRoutingConclusiveState as SuccessBasedRoutingConclusiveState,
        DbTokenizationFlag as TokenizationFlag, DbTotpStatus as TotpStatus,
        DbTransactionType as TransactionType, DbUserRoleVersion as UserRoleVersion,
        DbUserStatus as UserStatus, DbWebhookDeliveryAttempt as WebhookDeliveryAttempt,
    };
}

pub use common_enums::*;
use common_utils::pii;
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
pub use common_utils::tokenization;
use diesel::{deserialize::FromSqlRow, expression::AsExpression, sql_types::Jsonb};
use router_derive::diesel_enum;
use time::PrimitiveDateTime;

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RoutingAlgorithmKind {
    Single,
    Priority,
    VolumeSplit,
    Advanced,
    Dynamic,
    ThreeDsDecisionRule,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum EventObjectType {
    PaymentDetails,
    RefundDetails,
    DisputeDetails,
    MandateDetails,
    PayoutDetails,
}

// Refund
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum RefundType {
    InstantRefund,
    RegularRefund,
    RetryRefund,
}

// Mandate
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    Default,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum MandateType {
    SingleUse,
    #[default]
    MultiUse,
}

#[derive(
    serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression,
)]
#[diesel(sql_type = Jsonb)]
#[serde(rename_all = "snake_case")]
pub struct MandateDetails {
    pub update_mandate_id: Option<String>,
}

common_utils::impl_to_sql_from_sql_json!(MandateDetails);

#[derive(
    serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression,
)]
#[diesel(sql_type = Jsonb)]
#[serde(rename_all = "snake_case")]
pub enum MandateDataType {
    SingleUse(MandateAmountData),
    MultiUse(Option<MandateAmountData>),
}

common_utils::impl_to_sql_from_sql_json!(MandateDataType);

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct MandateAmountData {
    pub amount: common_utils::types::MinorUnit,
    pub currency: Currency,
    pub start_date: Option<PrimitiveDateTime>,
    pub end_date: Option<PrimitiveDateTime>,
    pub metadata: Option<pii::SecretSerdeValue>,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum FraudCheckType {
    PreFrm,
    PostFrm,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "text")]
#[strum(serialize_all = "snake_case")]
pub enum FraudCheckLastStep {
    #[default]
    Processing,
    CheckoutOrSale,
    TransactionOrRecordRefund,
    Fulfillment,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum UserStatus {
    Active,
    #[default]
    InvitationSent,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum DashboardMetadata {
    ProductionAgreement,
    SetupProcessor,
    ConfigureEndpoint,
    SetupComplete,
    FirstProcessorConnected,
    SecondProcessorConnected,
    ConfiguredRouting,
    TestPayment,
    IntegrationMethod,
    ConfigurationType,
    IntegrationCompleted,
    StripeConnected,
    PaypalConnected,
    SpRoutingConfigured,
    Feedback,
    ProdIntent,
    SpTestPayment,
    DownloadWoocom,
    ConfigureWoocom,
    SetupWoocomWebhook,
    IsMultipleConfiguration,
    IsChangePasswordRequired,
    OnboardingSurvey,
    ReconStatus,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum TotpStatus {
    Set,
    InProgress,
    #[default]
    NotSet,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
    strum::EnumString,
    strum::Display,
)]
#[diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum UserRoleVersion {
    #[default]
    V1,
    V2,
}


// File: crates/common_utils/src/crypto.rs
// Module: common_utils
// Public functions: 10
// Public structs: 14

//! Utilities for cryptographic algorithms
use std::ops::Deref;

use base64::Engine;
use error_stack::ResultExt;
use masking::{ExposeInterface, Secret};
use md5;
use pem;
use ring::{
    aead::{self, BoundKey, OpeningKey, SealingKey, UnboundKey},
    hmac, rand as ring_rand,
    signature::{RsaKeyPair, RSA_PSS_SHA256},
};
#[cfg(feature = "logs")]
use router_env::logger;
use rsa::{pkcs8::DecodePublicKey, signature::Verifier};

use crate::{
    consts::BASE64_ENGINE,
    errors::{self, CustomResult},
    pii::{self, EncryptionStrategy},
};

#[derive(Clone, Debug)]
struct NonceSequence(u128);

impl NonceSequence {
    /// Byte index at which sequence number starts in a 16-byte (128-bit) sequence.
    /// This byte index considers the big endian order used while encoding and decoding the nonce
    /// to/from a 128-bit unsigned integer.
    const SEQUENCE_NUMBER_START_INDEX: usize = 4;

    /// Generate a random nonce sequence.
    fn new() -> Result<Self, ring::error::Unspecified> {
        use ring::rand::{SecureRandom, SystemRandom};

        let rng = SystemRandom::new();

        // 96-bit sequence number, stored in a 128-bit unsigned integer in big-endian order
        let mut sequence_number = [0_u8; 128 / 8];
        rng.fill(&mut sequence_number[Self::SEQUENCE_NUMBER_START_INDEX..])?;
        let sequence_number = u128::from_be_bytes(sequence_number);

        Ok(Self(sequence_number))
    }

    /// Returns the current nonce value as bytes.
    fn current(&self) -> [u8; aead::NONCE_LEN] {
        let mut nonce = [0_u8; aead::NONCE_LEN];
        nonce.copy_from_slice(&self.0.to_be_bytes()[Self::SEQUENCE_NUMBER_START_INDEX..]);
        nonce
    }

    /// Constructs a nonce sequence from bytes
    fn from_bytes(bytes: [u8; aead::NONCE_LEN]) -> Self {
        let mut sequence_number = [0_u8; 128 / 8];
        sequence_number[Self::SEQUENCE_NUMBER_START_INDEX..].copy_from_slice(&bytes);
        let sequence_number = u128::from_be_bytes(sequence_number);
        Self(sequence_number)
    }
}

impl aead::NonceSequence for NonceSequence {
    fn advance(&mut self) -> Result<aead::Nonce, ring::error::Unspecified> {
        let mut nonce = [0_u8; aead::NONCE_LEN];
        nonce.copy_from_slice(&self.0.to_be_bytes()[Self::SEQUENCE_NUMBER_START_INDEX..]);

        // Increment sequence number
        self.0 = self.0.wrapping_add(1);

        // Return previous sequence number as bytes
        Ok(aead::Nonce::assume_unique_for_key(nonce))
    }
}

/// Trait for cryptographically signing messages
pub trait SignMessage {
    /// Takes in a secret and a message and returns the calculated signature as bytes
    fn sign_message(
        &self,
        _secret: &[u8],
        _msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError>;
}

/// Trait for cryptographically verifying a message against a signature
pub trait VerifySignature {
    /// Takes in a secret, the signature and the message and verifies the message
    /// against the signature
    fn verify_signature(
        &self,
        _secret: &[u8],
        _signature: &[u8],
        _msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError>;
}

/// Trait for cryptographically encoding a message
pub trait EncodeMessage {
    /// Takes in a secret and the message and encodes it, returning bytes
    fn encode_message(
        &self,
        _secret: &[u8],
        _msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError>;
}

/// Trait for cryptographically decoding a message
pub trait DecodeMessage {
    /// Takes in a secret, an encoded messages and attempts to decode it, returning bytes
    fn decode_message(
        &self,
        _secret: &[u8],
        _msg: Secret<Vec<u8>, EncryptionStrategy>,
    ) -> CustomResult<Vec<u8>, errors::CryptoError>;
}

/// Represents no cryptographic algorithm.
/// Implements all crypto traits and acts like a Nop
#[derive(Debug)]
pub struct NoAlgorithm;

impl SignMessage for NoAlgorithm {
    fn sign_message(
        &self,
        _secret: &[u8],
        _msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        Ok(Vec::new())
    }
}

impl VerifySignature for NoAlgorithm {
    fn verify_signature(
        &self,
        _secret: &[u8],
        _signature: &[u8],
        _msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError> {
        Ok(true)
    }
}

impl EncodeMessage for NoAlgorithm {
    fn encode_message(
        &self,
        _secret: &[u8],
        msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        Ok(msg.to_vec())
    }
}

impl DecodeMessage for NoAlgorithm {
    fn decode_message(
        &self,
        _secret: &[u8],
        msg: Secret<Vec<u8>, EncryptionStrategy>,
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        Ok(msg.expose())
    }
}

/// Represents the HMAC-SHA-1 algorithm
#[derive(Debug)]
pub struct HmacSha1;

impl SignMessage for HmacSha1 {
    fn sign_message(
        &self,
        secret: &[u8],
        msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let key = hmac::Key::new(hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY, secret);
        Ok(hmac::sign(&key, msg).as_ref().to_vec())
    }
}

impl VerifySignature for HmacSha1 {
    fn verify_signature(
        &self,
        secret: &[u8],
        signature: &[u8],
        msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError> {
        let key = hmac::Key::new(hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY, secret);

        Ok(hmac::verify(&key, msg, signature).is_ok())
    }
}

/// Represents the HMAC-SHA-256 algorithm
#[derive(Debug)]
pub struct HmacSha256;

impl SignMessage for HmacSha256 {
    fn sign_message(
        &self,
        secret: &[u8],
        msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let key = hmac::Key::new(hmac::HMAC_SHA256, secret);
        Ok(hmac::sign(&key, msg).as_ref().to_vec())
    }
}

impl VerifySignature for HmacSha256 {
    fn verify_signature(
        &self,
        secret: &[u8],
        signature: &[u8],
        msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError> {
        let key = hmac::Key::new(hmac::HMAC_SHA256, secret);

        Ok(hmac::verify(&key, msg, signature).is_ok())
    }
}

/// Represents the HMAC-SHA-512 algorithm
#[derive(Debug)]
pub struct HmacSha512;

impl SignMessage for HmacSha512 {
    fn sign_message(
        &self,
        secret: &[u8],
        msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let key = hmac::Key::new(hmac::HMAC_SHA512, secret);
        Ok(hmac::sign(&key, msg).as_ref().to_vec())
    }
}

impl VerifySignature for HmacSha512 {
    fn verify_signature(
        &self,
        secret: &[u8],
        signature: &[u8],
        msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError> {
        let key = hmac::Key::new(hmac::HMAC_SHA512, secret);

        Ok(hmac::verify(&key, msg, signature).is_ok())
    }
}

/// Blake3
#[derive(Debug)]
pub struct Blake3(String);

impl Blake3 {
    /// Create a new instance of Blake3 with a key
    pub fn new(key: impl Into<String>) -> Self {
        Self(key.into())
    }
}

impl SignMessage for Blake3 {
    fn sign_message(
        &self,
        secret: &[u8],
        msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let key = blake3::derive_key(&self.0, secret);
        let output = blake3::keyed_hash(&key, msg).as_bytes().to_vec();
        Ok(output)
    }
}

impl VerifySignature for Blake3 {
    fn verify_signature(
        &self,
        secret: &[u8],
        signature: &[u8],
        msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError> {
        let key = blake3::derive_key(&self.0, secret);
        let output = blake3::keyed_hash(&key, msg);
        Ok(output.as_bytes() == signature)
    }
}

/// Represents the GCM-AES-256 algorithm
#[derive(Debug)]
pub struct GcmAes256;

impl EncodeMessage for GcmAes256 {
    fn encode_message(
        &self,
        secret: &[u8],
        msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let nonce_sequence =
            NonceSequence::new().change_context(errors::CryptoError::EncodingFailed)?;
        let current_nonce = nonce_sequence.current();
        let key = UnboundKey::new(&aead::AES_256_GCM, secret)
            .change_context(errors::CryptoError::EncodingFailed)?;
        let mut key = SealingKey::new(key, nonce_sequence);
        let mut in_out = msg.to_vec();

        key.seal_in_place_append_tag(aead::Aad::empty(), &mut in_out)
            .change_context(errors::CryptoError::EncodingFailed)?;
        in_out.splice(0..0, current_nonce);

        Ok(in_out)
    }
}

impl DecodeMessage for GcmAes256 {
    fn decode_message(
        &self,
        secret: &[u8],
        msg: Secret<Vec<u8>, EncryptionStrategy>,
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let msg = msg.expose();
        let key = UnboundKey::new(&aead::AES_256_GCM, secret)
            .change_context(errors::CryptoError::DecodingFailed)?;

        let nonce_sequence = NonceSequence::from_bytes(
            <[u8; aead::NONCE_LEN]>::try_from(
                msg.get(..aead::NONCE_LEN)
                    .ok_or(errors::CryptoError::DecodingFailed)
                    .attach_printable("Failed to read the nonce form the encrypted ciphertext")?,
            )
            .change_context(errors::CryptoError::DecodingFailed)?,
        );

        let mut key = OpeningKey::new(key, nonce_sequence);
        let mut binding = msg;
        let output = binding.as_mut_slice();

        let result = key
            .open_within(aead::Aad::empty(), output, aead::NONCE_LEN..)
            .change_context(errors::CryptoError::DecodingFailed)?;

        Ok(result.to_vec())
    }
}
/// Represents the ED25519 signature verification algorithm
#[derive(Debug)]
pub struct Ed25519;

impl Ed25519 {
    /// ED25519 algorithm constants
    const ED25519_PUBLIC_KEY_LEN: usize = 32;
    const ED25519_SIGNATURE_LEN: usize = 64;
    /// Validates ED25519 inputs (public key and signature lengths)
    fn validate_inputs(
        public_key: &[u8],
        signature: &[u8],
    ) -> CustomResult<(), errors::CryptoError> {
        // Validate public key length
        if public_key.len() != Self::ED25519_PUBLIC_KEY_LEN {
            return Err(errors::CryptoError::InvalidKeyLength).attach_printable(format!(
                "Invalid ED25519 public key length: expected {} bytes, got {}",
                Self::ED25519_PUBLIC_KEY_LEN,
                public_key.len()
            ));
        }

        // Validate signature length
        if signature.len() != Self::ED25519_SIGNATURE_LEN {
            return Err(errors::CryptoError::InvalidKeyLength).attach_printable(format!(
                "Invalid ED25519 signature length: expected {} bytes, got {}",
                Self::ED25519_SIGNATURE_LEN,
                signature.len()
            ));
        }

        Ok(())
    }
}

impl VerifySignature for Ed25519 {
    fn verify_signature(
        &self,
        public_key: &[u8],
        signature: &[u8], // ED25519 signature bytes (must be 64 bytes)
        msg: &[u8],       // Message that was signed
    ) -> CustomResult<bool, errors::CryptoError> {
        // Validate inputs first
        Self::validate_inputs(public_key, signature)?;

        // Create unparsed public key
        let ring_public_key =
            ring::signature::UnparsedPublicKey::new(&ring::signature::ED25519, public_key);

        // Perform verification
        match ring_public_key.verify(msg, signature) {
            Ok(()) => Ok(true),
            Err(_err) => {
                #[cfg(feature = "logs")]
                logger::error!("ED25519 signature verification failed: {:?}", _err);
                Err(errors::CryptoError::SignatureVerificationFailed)
                    .attach_printable("ED25519 signature verification failed")
            }
        }
    }
}

impl SignMessage for Ed25519 {
    fn sign_message(
        &self,
        secret: &[u8],
        msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        if secret.len() != 32 {
            return Err(errors::CryptoError::InvalidKeyLength).attach_printable(format!(
                "Invalid ED25519 private key length: expected 32 bytes, got {}",
                secret.len()
            ));
        }
        let key_pair = ring::signature::Ed25519KeyPair::from_seed_unchecked(secret)
            .change_context(errors::CryptoError::MessageSigningFailed)
            .attach_printable("Failed to create ED25519 key pair from seed")?;

        let signature = key_pair.sign(msg);
        Ok(signature.as_ref().to_vec())
    }
}
/// Secure Hash Algorithm 512
#[derive(Debug)]
pub struct Sha512;

/// Secure Hash Algorithm 256
#[derive(Debug)]
pub struct Sha256;

/// Trait for generating a digest for SHA
pub trait GenerateDigest {
    /// takes a message and creates a digest for it
    fn generate_digest(&self, message: &[u8]) -> CustomResult<Vec<u8>, errors::CryptoError>;
}

impl GenerateDigest for Sha512 {
    fn generate_digest(&self, message: &[u8]) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let digest = ring::digest::digest(&ring::digest::SHA512, message);
        Ok(digest.as_ref().to_vec())
    }
}
impl VerifySignature for Sha512 {
    fn verify_signature(
        &self,
        _secret: &[u8],
        signature: &[u8],
        msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError> {
        let msg_str = std::str::from_utf8(msg)
            .change_context(errors::CryptoError::EncodingFailed)?
            .to_owned();
        let hashed_digest = hex::encode(
            Self.generate_digest(msg_str.as_bytes())
                .change_context(errors::CryptoError::SignatureVerificationFailed)?,
        );
        let hashed_digest_into_bytes = hashed_digest.into_bytes();
        Ok(hashed_digest_into_bytes == signature)
    }
}
/// MD5 hash function
#[derive(Debug)]
pub struct Md5;

impl GenerateDigest for Md5 {
    fn generate_digest(&self, message: &[u8]) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let digest = md5::compute(message);
        Ok(digest.as_ref().to_vec())
    }
}

impl VerifySignature for Md5 {
    fn verify_signature(
        &self,
        _secret: &[u8],
        signature: &[u8],
        msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError> {
        let hashed_digest = Self
            .generate_digest(msg)
            .change_context(errors::CryptoError::SignatureVerificationFailed)?;
        Ok(hashed_digest == signature)
    }
}

impl GenerateDigest for Sha256 {
    fn generate_digest(&self, message: &[u8]) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let digest = ring::digest::digest(&ring::digest::SHA256, message);
        Ok(digest.as_ref().to_vec())
    }
}

impl VerifySignature for Sha256 {
    fn verify_signature(
        &self,
        _secret: &[u8],
        signature: &[u8],
        msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError> {
        let hashed_digest = Self
            .generate_digest(msg)
            .change_context(errors::CryptoError::SignatureVerificationFailed)?;
        let hashed_digest_into_bytes = hashed_digest.as_slice();
        Ok(hashed_digest_into_bytes == signature)
    }
}

/// Secure Hash Algorithm 256 with RSA public-key cryptosystem
#[derive(Debug)]
pub struct RsaSha256;

impl VerifySignature for RsaSha256 {
    fn verify_signature(
        &self,
        secret: &[u8],
        signature: &[u8],
        msg: &[u8],
    ) -> CustomResult<bool, errors::CryptoError> {
        // create verifying key
        let decoded_public_key = BASE64_ENGINE
            .decode(secret)
            .change_context(errors::CryptoError::SignatureVerificationFailed)
            .attach_printable("base64 decoding failed")?;

        let string_public_key = String::from_utf8(decoded_public_key)
            .change_context(errors::CryptoError::SignatureVerificationFailed)
            .attach_printable("utf8 to string parsing failed")?;

        let rsa_public_key = rsa::RsaPublicKey::from_public_key_pem(&string_public_key)
            .change_context(errors::CryptoError::SignatureVerificationFailed)
            .attach_printable("rsa public key transformation failed")?;

        let verifying_key = rsa::pkcs1v15::VerifyingKey::<rsa::sha2::Sha256>::new(rsa_public_key);

        // transfrom the signature
        let decoded_signature = BASE64_ENGINE
            .decode(signature)
            .change_context(errors::CryptoError::SignatureVerificationFailed)
            .attach_printable("base64 decoding failed")?;

        let rsa_signature = rsa::pkcs1v15::Signature::try_from(&decoded_signature[..])
            .change_context(errors::CryptoError::SignatureVerificationFailed)
            .attach_printable("rsa signature transformation failed")?;

        // signature verification
        verifying_key
            .verify(msg, &rsa_signature)
            .map(|_| true)
            .change_context(errors::CryptoError::SignatureVerificationFailed)
            .attach_printable("signature verification step failed")
    }
}

/// TripleDesEde3 hash function
#[derive(Debug)]
#[cfg(feature = "crypto_openssl")]
pub struct TripleDesEde3CBC {
    padding: common_enums::CryptoPadding,
    iv: Vec<u8>,
}

#[cfg(feature = "crypto_openssl")]
impl TripleDesEde3CBC {
    const TRIPLE_DES_KEY_LENGTH: usize = 24;
    /// Initialization Vector (IV) length for TripleDesEde3
    pub const TRIPLE_DES_IV_LENGTH: usize = 8;

    /// Constructor function to be used by the encryptor and decryptor to generate the data type
    pub fn new(
        padding: Option<common_enums::CryptoPadding>,
        iv: Vec<u8>,
    ) -> Result<Self, errors::CryptoError> {
        if iv.len() != Self::TRIPLE_DES_IV_LENGTH {
            Err(errors::CryptoError::InvalidIvLength)?
        };
        let padding = padding.unwrap_or(common_enums::CryptoPadding::PKCS7);
        Ok(Self { iv, padding })
    }
}

#[cfg(feature = "crypto_openssl")]
impl EncodeMessage for TripleDesEde3CBC {
    fn encode_message(
        &self,
        secret: &[u8],
        msg: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        if secret.len() != Self::TRIPLE_DES_KEY_LENGTH {
            Err(errors::CryptoError::InvalidKeyLength)?
        }
        let mut buffer = msg.to_vec();

        if let common_enums::CryptoPadding::ZeroPadding = self.padding {
            let pad_len = Self::TRIPLE_DES_IV_LENGTH - (buffer.len() % Self::TRIPLE_DES_IV_LENGTH);
            if pad_len != Self::TRIPLE_DES_IV_LENGTH {
                buffer.extend(vec![0u8; pad_len]);
            }
        };
        let cipher = openssl::symm::Cipher::des_ede3_cbc();
        openssl::symm::encrypt(cipher, secret, Some(&self.iv), &buffer)
            .change_context(errors::CryptoError::EncodingFailed)
    }
}

/// Generate a random string using a cryptographically secure pseudo-random number generator
/// (CSPRNG). Typically used for generating (readable) keys and passwords.
#[inline]
pub fn generate_cryptographically_secure_random_string(length: usize) -> String {
    use rand::distributions::DistString;

    rand::distributions::Alphanumeric.sample_string(&mut rand::rngs::OsRng, length)
}

/// Generate an array of random bytes using a cryptographically secure pseudo-random number
/// generator (CSPRNG). Typically used for generating keys.
#[inline]
pub fn generate_cryptographically_secure_random_bytes<const N: usize>() -> [u8; N] {
    use rand::RngCore;

    let mut bytes = [0; N];
    rand::rngs::OsRng.fill_bytes(&mut bytes);
    bytes
}

/// A wrapper type to store the encrypted data for sensitive pii domain data types
#[derive(Debug, Clone)]
pub struct Encryptable<T: Clone> {
    inner: T,
    encrypted: Secret<Vec<u8>, EncryptionStrategy>,
}

impl<T: Clone, S: masking::Strategy<T>> Encryptable<Secret<T, S>> {
    /// constructor function to be used by the encryptor and decryptor to generate the data type
    pub fn new(
        masked_data: Secret<T, S>,
        encrypted_data: Secret<Vec<u8>, EncryptionStrategy>,
    ) -> Self {
        Self {
            inner: masked_data,
            encrypted: encrypted_data,
        }
    }
}

impl<T: Clone> Encryptable<T> {
    /// Get the inner data while consuming self
    #[inline]
    pub fn into_inner(self) -> T {
        self.inner
    }

    /// Get the reference to inner value
    #[inline]
    pub fn get_inner(&self) -> &T {
        &self.inner
    }

    /// Get the inner encrypted data while consuming self
    #[inline]
    pub fn into_encrypted(self) -> Secret<Vec<u8>, EncryptionStrategy> {
        self.encrypted
    }

    /// Deserialize inner value and return new Encryptable object
    pub fn deserialize_inner_value<U, F>(
        self,
        f: F,
    ) -> CustomResult<Encryptable<U>, errors::ParsingError>
    where
        F: FnOnce(T) -> CustomResult<U, errors::ParsingError>,
        U: Clone,
    {
        let inner = self.inner;
        let encrypted = self.encrypted;
        let inner = f(inner)?;
        Ok(Encryptable { inner, encrypted })
    }

    /// consume self and modify the inner value
    pub fn map<U: Clone>(self, f: impl FnOnce(T) -> U) -> Encryptable<U> {
        let encrypted_data = self.encrypted;
        let masked_data = f(self.inner);
        Encryptable {
            inner: masked_data,
            encrypted: encrypted_data,
        }
    }
}

impl<T: Clone> Deref for Encryptable<Secret<T>> {
    type Target = Secret<T>;
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<T: Clone> masking::Serialize for Encryptable<T>
where
    T: masking::Serialize,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.inner.serialize(serializer)
    }
}

impl<T: Clone> PartialEq for Encryptable<T>
where
    T: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.inner.eq(&other.inner)
    }
}

/// Type alias for `Option<Encryptable<Secret<String>>>`
pub type OptionalEncryptableSecretString = Option<Encryptable<Secret<String>>>;
/// Type alias for `Option<Encryptable<Secret<String>>>` used for `name` field
pub type OptionalEncryptableName = Option<Encryptable<Secret<String>>>;
/// Type alias for `Option<Encryptable<Secret<String>>>` used for `email` field
pub type OptionalEncryptableEmail = Option<Encryptable<Secret<String, pii::EmailStrategy>>>;
/// Type alias for `Option<Encryptable<Secret<String>>>` used for `phone` field
pub type OptionalEncryptablePhone = Option<Encryptable<Secret<String>>>;
/// Type alias for `Option<Encryptable<Secret<serde_json::Value>>>`
pub type OptionalEncryptableValue = Option<Encryptable<Secret<serde_json::Value>>>;
/// Type alias for `Option<Secret<serde_json::Value>>`
pub type OptionalSecretValue = Option<Secret<serde_json::Value>>;
/// Type alias for `Encryptable<Secret<String>>` used for `name` field
pub type EncryptableName = Encryptable<Secret<String>>;
/// Type alias for `Encryptable<Secret<String>>` used for `email` field
pub type EncryptableEmail = Encryptable<Secret<String, pii::EmailStrategy>>;

/// Represents the RSA-PSS-SHA256 signing algorithm
#[derive(Debug)]
pub struct RsaPssSha256;

impl SignMessage for RsaPssSha256 {
    fn sign_message(
        &self,
        private_key_pem_bytes: &[u8],
        msg_to_sign: &[u8],
    ) -> CustomResult<Vec<u8>, errors::CryptoError> {
        let parsed_pem = pem::parse(private_key_pem_bytes)
            .change_context(errors::CryptoError::EncodingFailed)
            .attach_printable("Failed to parse PEM string")?;
        let key_pair = match parsed_pem.tag() {
            "PRIVATE KEY" => RsaKeyPair::from_pkcs8(parsed_pem.contents())
                .change_context(errors::CryptoError::InvalidKeyLength)
                .attach_printable("Failed to parse PKCS#8 DER with ring"),
            "RSA PRIVATE KEY" => RsaKeyPair::from_der(parsed_pem.contents())
                .change_context(errors::CryptoError::InvalidKeyLength)
                .attach_printable("Failed to parse PKCS#1 DER (using from_der) with ring"),
            tag => Err(errors::CryptoError::InvalidKeyLength).attach_printable(format!(
                "Unexpected PEM tag: {tag}. Expected 'PRIVATE KEY' or 'RSA PRIVATE KEY'",
            )),
        }?;
        let rng = ring_rand::SystemRandom::new();
        let signature_len = key_pair.public().modulus_len();
        let mut signature_bytes = vec![0; signature_len];
        key_pair
            .sign(&RSA_PSS_SHA256, &rng, msg_to_sign, &mut signature_bytes)
            .change_context(errors::CryptoError::EncodingFailed)
            .attach_printable("Failed to sign data with ring")?;

        Ok(signature_bytes)
    }
}

#[cfg(test)]
mod crypto_tests {
    #![allow(clippy::expect_used)]
    use super::{DecodeMessage, EncodeMessage, SignMessage, VerifySignature};
    use crate::crypto::GenerateDigest;

    #[test]
    fn test_hmac_sha256_sign_message() {
        let message = r#"{"type":"payment_intent"}"#.as_bytes();
        let secret = "hmac_secret_1234".as_bytes();
        let right_signature =
            hex::decode("d5550730377011948f12cc28889bee590d2a5434d6f54b87562f2dbc2657823e")
                .expect("Right signature decoding");

        let signature = super::HmacSha256
            .sign_message(secret, message)
            .expect("Signature");

        assert_eq!(signature, right_signature);
    }

    #[test]
    fn test_hmac_sha256_verify_signature() {
        let right_signature =
            hex::decode("d5550730377011948f12cc28889bee590d2a5434d6f54b87562f2dbc2657823e")
                .expect("Right signature decoding");
        let wrong_signature =
            hex::decode("d5550730377011948f12cc28889bee590d2a5434d6f54b87562f2dbc2657823f")
                .expect("Wrong signature decoding");
        let secret = "hmac_secret_1234".as_bytes();
        let data = r#"{"type":"payment_intent"}"#.as_bytes();

        let right_verified = super::HmacSha256
            .verify_signature(secret, &right_signature, data)
            .expect("Right signature verification result");

        assert!(right_verified);

        let wrong_verified = super::HmacSha256
            .verify_signature(secret, &wrong_signature, data)
            .expect("Wrong signature verification result");

        assert!(!wrong_verified);
    }

    #[test]
    fn test_sha256_verify_signature() {
        let right_signature =
            hex::decode("123250a72f4e961f31661dbcee0fec0f4714715dc5ae1b573f908a0a5381ddba")
                .expect("Right signature decoding");
        let wrong_signature =
            hex::decode("123250a72f4e961f31661dbcee0fec0f4714715dc5ae1b573f908a0a5381ddbb")
                .expect("Wrong signature decoding");
        let secret = "".as_bytes();
        let data = r#"AJHFH9349JASFJHADJ9834115USD2020-11-13.13:22:34711000000021406655APPROVED12345product_id"#.as_bytes();

        let right_verified = super::Sha256
            .verify_signature(secret, &right_signature, data)
            .expect("Right signature verification result");

        assert!(right_verified);

        let wrong_verified = super::Sha256
            .verify_signature(secret, &wrong_signature, data)
            .expect("Wrong signature verification result");

        assert!(!wrong_verified);
    }

    #[test]
    fn test_hmac_sha512_sign_message() {
        let message = r#"{"type":"payment_intent"}"#.as_bytes();
        let secret = "hmac_secret_1234".as_bytes();
        let right_signature = hex::decode("38b0bc1ea66b14793e39cd58e93d37b799a507442d0dd8d37443fa95dec58e57da6db4742636fea31201c48e57a66e73a308a2e5a5c6bb831e4e39fe2227c00f")
            .expect("signature decoding");

        let signature = super::HmacSha512
            .sign_message(secret, message)
            .expect("Signature");

        assert_eq!(signature, right_signature);
    }

    #[test]
    fn test_hmac_sha512_verify_signature() {
        let right_signature = hex::decode("38b0bc1ea66b14793e39cd58e93d37b799a507442d0dd8d37443fa95dec58e57da6db4742636fea31201c48e57a66e73a308a2e5a5c6bb831e4e39fe2227c00f")
            .expect("signature decoding");
        let wrong_signature =
            hex::decode("d5550730377011948f12cc28889bee590d2a5434d6f54b87562f2dbc2657823f")
                .expect("Wrong signature decoding");
        let secret = "hmac_secret_1234".as_bytes();
        let data = r#"{"type":"payment_intent"}"#.as_bytes();

        let right_verified = super::HmacSha512
            .verify_signature(secret, &right_signature, data)
            .expect("Right signature verification result");

        assert!(right_verified);

        let wrong_verified = super::HmacSha256
            .verify_signature(secret, &wrong_signature, data)
            .expect("Wrong signature verification result");

        assert!(!wrong_verified);
    }

    #[test]
    fn test_gcm_aes_256_encode_message() {
        let message = r#"{"type":"PAYMENT"}"#.as_bytes();
        let secret =
            hex::decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f")
                .expect("Secret decoding");
        let algorithm = super::GcmAes256;

        let encoded_message = algorithm
            .encode_message(&secret, message)
            .expect("Encoded message and tag");

        assert_eq!(
            algorithm
                .decode_message(&secret, encoded_message.into())
                .expect("Decode Failed"),
            message
        );
    }

    #[test]
    fn test_gcm_aes_256_decode_message() {
        // Inputs taken from AES GCM test vectors provided by NIST
        // https://github.com/briansmith/ring/blob/95948b3977013aed16db92ae32e6b8384496a740/tests/aead_aes_256_gcm_tests.txt#L447-L452

        let right_secret =
            hex::decode("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308")
                .expect("Secret decoding");
        let wrong_secret =
            hex::decode("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308309")
                .expect("Secret decoding");
        let message =


// File: crates/router/src/core/payments/operations.rs
// Module: router
// Public structs: 4

#[cfg(feature = "v1")]
pub mod payment_approve;
#[cfg(feature = "v1")]
pub mod payment_cancel;
#[cfg(feature = "v1")]
pub mod payment_cancel_post_capture;
#[cfg(feature = "v1")]
pub mod payment_capture;
#[cfg(feature = "v1")]
pub mod payment_complete_authorize;
#[cfg(feature = "v1")]
pub mod payment_confirm;
#[cfg(feature = "v1")]
pub mod payment_create;
#[cfg(feature = "v1")]
pub mod payment_post_session_tokens;
#[cfg(feature = "v1")]
pub mod payment_reject;
pub mod payment_response;
#[cfg(feature = "v1")]
pub mod payment_session;
#[cfg(feature = "v2")]
pub mod payment_session_intent;
#[cfg(feature = "v1")]
pub mod payment_start;
#[cfg(feature = "v1")]
pub mod payment_status;
#[cfg(feature = "v1")]
pub mod payment_update;
#[cfg(feature = "v1")]
pub mod payment_update_metadata;
#[cfg(feature = "v1")]
pub mod payments_incremental_authorization;
#[cfg(feature = "v1")]
pub mod tax_calculation;

#[cfg(feature = "v2")]
pub mod payment_attempt_list;
#[cfg(feature = "v2")]
pub mod payment_attempt_record;
#[cfg(feature = "v2")]
pub mod payment_confirm_intent;
#[cfg(feature = "v2")]
pub mod payment_create_intent;
#[cfg(feature = "v2")]
pub mod payment_get_intent;
#[cfg(feature = "v2")]
pub mod payment_update_intent;
#[cfg(feature = "v2")]
pub mod proxy_payments_intent;

#[cfg(feature = "v2")]
pub mod external_vault_proxy_payment_intent;

#[cfg(feature = "v2")]
pub mod payment_get;

#[cfg(feature = "v2")]
pub mod payment_capture_v2;

#[cfg(feature = "v2")]
pub mod payment_cancel_v2;

use api_models::enums::FrmSuggestion;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use api_models::routing::RoutableConnectorChoice;
use async_trait::async_trait;
use error_stack::{report, ResultExt};
use router_env::{instrument, tracing};

#[cfg(feature = "v2")]
pub use self::payment_attempt_list::PaymentGetListAttempts;
#[cfg(feature = "v2")]
pub use self::payment_get::PaymentGet;
#[cfg(feature = "v2")]
pub use self::payment_get_intent::PaymentGetIntent;
pub use self::payment_response::PaymentResponse;
#[cfg(feature = "v2")]
pub use self::payment_update_intent::PaymentUpdateIntent;
#[cfg(feature = "v1")]
pub use self::{
    payment_approve::PaymentApprove, payment_cancel::PaymentCancel,
    payment_cancel_post_capture::PaymentCancelPostCapture, payment_capture::PaymentCapture,
    payment_confirm::PaymentConfirm, payment_create::PaymentCreate,
    payment_post_session_tokens::PaymentPostSessionTokens, payment_reject::PaymentReject,
    payment_session::PaymentSession, payment_start::PaymentStart, payment_status::PaymentStatus,
    payment_update::PaymentUpdate, payment_update_metadata::PaymentUpdateMetadata,
    payments_incremental_authorization::PaymentIncrementalAuthorization,
    tax_calculation::PaymentSessionUpdate,
};
#[cfg(feature = "v2")]
pub use self::{
    payment_confirm_intent::PaymentIntentConfirm, payment_create_intent::PaymentIntentCreate,
    payment_session_intent::PaymentSessionIntent,
};
use super::{helpers, CustomerDetails, OperationSessionGetters, OperationSessionSetters};
#[cfg(feature = "v2")]
use crate::core::payments;
use crate::{
    core::errors::{self, CustomResult, RouterResult},
    routes::{app::ReqState, SessionState},
    services,
    types::{
        self,
        api::{self, ConnectorCallType},
        domain,
        storage::{self, enums},
        PaymentsResponseData,
    },
};

pub type BoxedOperation<'a, F, T, D> = Box<dyn Operation<F, T, Data = D> + Send + Sync + 'a>;

pub trait Operation<F: Clone, T>: Send + std::fmt::Debug {
    type Data;
    fn to_validate_request(
        &self,
    ) -> RouterResult<&(dyn ValidateRequest<F, T, Self::Data> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("validate request interface not found for {self:?}"))
    }

    fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<F, Self::Data, T> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("get tracker interface not found for {self:?}"))
    }

    fn to_domain(&self) -> RouterResult<&dyn Domain<F, T, Self::Data>> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("domain interface not found for {self:?}"))
    }

    fn to_update_tracker(
        &self,
    ) -> RouterResult<&(dyn UpdateTracker<F, Self::Data, T> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("update tracker interface not found for {self:?}"))
    }

    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<&(dyn PostUpdateTracker<F, Self::Data, T> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError)).attach_printable_lazy(|| {
            format!("post connector update tracker not found for {self:?}")
        })
    }
}

#[cfg(feature = "v1")]
#[derive(Clone)]
pub struct ValidateResult {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: api::PaymentIdType,
    pub storage_scheme: enums::MerchantStorageScheme,
    pub requeue: bool,
}

#[cfg(feature = "v2")]
#[derive(Clone)]
pub struct ValidateResult {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub storage_scheme: enums::MerchantStorageScheme,
    pub requeue: bool,
}

#[cfg(feature = "v1")]
#[allow(clippy::type_complexity)]
pub trait ValidateRequest<F, R, D> {
    fn validate_request<'b>(
        &'b self,
        request: &R,
        merchant_context: &domain::MerchantContext,
    ) -> RouterResult<(BoxedOperation<'b, F, R, D>, ValidateResult)>;
}

#[cfg(feature = "v2")]
pub trait ValidateRequest<F, R, D> {
    fn validate_request(
        &self,
        request: &R,
        merchant_context: &domain::MerchantContext,
    ) -> RouterResult<ValidateResult>;
}

#[cfg(feature = "v2")]
pub struct GetTrackerResponse<D> {
    pub payment_data: D,
}

#[cfg(feature = "v1")]
pub struct GetTrackerResponse<'a, F: Clone, R, D> {
    pub operation: BoxedOperation<'a, F, R, D>,
    pub customer_details: Option<CustomerDetails>,
    pub payment_data: D,
    pub business_profile: domain::Profile,
    pub mandate_type: Option<api::MandateTransactionType>,
}

/// This trait is used to fetch / create all the tracker related information for a payment
/// This functions returns the session data that is used by subsequent functions
#[async_trait]
pub trait GetTracker<F: Clone, D, R>: Send {
    #[cfg(feature = "v1")]
    #[allow(clippy::too_many_arguments)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &api::PaymentIdType,
        request: &R,
        merchant_context: &domain::MerchantContext,
        auth_flow: services::AuthFlow,
        header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<GetTrackerResponse<'a, F, R, D>>;

    #[cfg(feature = "v2")]
    #[allow(clippy::too_many_arguments)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &common_utils::id_type::GlobalPaymentId,
        request: &R,
        merchant_context: &domain::MerchantContext,
        profile: &domain::Profile,
        header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<GetTrackerResponse<D>>;

    async fn validate_request_with_state(
        &self,
        _state: &SessionState,
        _request: &R,
        _payment_data: &mut D,
        _business_profile: &domain::Profile,
    ) -> RouterResult<()> {
        Ok(())
    }
}

#[async_trait]
pub trait Domain<F: Clone, R, D>: Send + Sync {
    #[cfg(feature = "v1")]
    /// This will fetch customer details, (this operation is flow specific)
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        request: Option<CustomerDetails>,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<(BoxedOperation<'a, F, R, D>, Option<domain::Customer>), errors::StorageError>;

    #[cfg(feature = "v2")]
    /// This will fetch customer details, (this operation is flow specific)
    async fn get_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<(BoxedOperation<'a, F, R, D>, Option<domain::Customer>), errors::StorageError>;

    #[cfg(feature = "v2")]
    /// This will run the decision manager for the payment
    async fn run_decision_manager<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        business_profile: &domain::Profile,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    async fn make_pm_data<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: &mut D,
        storage_scheme: enums::MerchantStorageScheme,
        merchant_key_store: &domain::MerchantKeyStore,
        customer: &Option<domain::Customer>,
        business_profile: &domain::Profile,
        should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, R, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )>;

    async fn add_task_to_process_tracker<'a>(
        &'a self,
        _db: &'a SessionState,
        _payment_attempt: &storage::PaymentAttempt,
        _requeue: bool,
        _schedule_time: Option<time::PrimitiveDateTime>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[cfg(feature = "v1")]
    async fn get_connector<'a>(
        &'a self,
        merchant_context: &domain::MerchantContext,
        state: &SessionState,
        request: &R,
        payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse>;

    #[cfg(feature = "v2")]
    async fn get_connector_from_request<'a>(
        &'a self,
        state: &SessionState,
        request: &R,
        payment_data: &mut D,
    ) -> CustomResult<api::ConnectorData, errors::ApiErrorResponse> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| "get connector for tunnel not implemented".to_string())
    }

    #[cfg(feature = "v2")]
    async fn perform_routing<'a>(
        &'a self,
        merchant_context: &domain::MerchantContext,
        business_profile: &domain::Profile,
        state: &SessionState,
        // TODO: do not take the whole payment data here
        payment_data: &mut D,
    ) -> CustomResult<ConnectorCallType, errors::ApiErrorResponse>;

    async fn populate_payment_data<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _merchant_context: &domain::MerchantContext,
        _business_profile: &domain::Profile,
        _connector_data: &api::ConnectorData,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    async fn call_external_three_ds_authentication_if_eligible<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _should_continue_confirm_transaction: &mut bool,
        _connector_call_type: &ConnectorCallType,
        _business_profile: &domain::Profile,
        _key_store: &domain::MerchantKeyStore,
        _mandate_type: Option<api_models::payments::MandateTransactionType>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    async fn call_unified_authentication_service_if_eligible<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _should_continue_confirm_transaction: &mut bool,
        _connector_call_type: &ConnectorCallType,
        _business_profile: &domain::Profile,
        _key_store: &domain::MerchantKeyStore,
        _mandate_type: Option<api_models::payments::MandateTransactionType>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    async fn payments_dynamic_tax_calculation<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _connector_call_type: &ConnectorCallType,
        _business_profile: &domain::Profile,
        _merchant_context: &domain::MerchantContext,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }

    async fn store_extended_card_info_temporarily<'a>(
        &'a self,
        _state: &SessionState,
        _payment_id: &common_utils::id_type::PaymentId,
        _business_profile: &domain::Profile,
        _payment_method_data: Option<&domain::PaymentMethodData>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[cfg(feature = "v2")]
    async fn create_or_fetch_payment_method<'a>(
        &'a self,
        state: &SessionState,
        merchant_context: &domain::MerchantContext,
        business_profile: &domain::Profile,
        payment_data: &mut D,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    // does not propagate error to not affect the payment flow
    // must add debugger in case of internal error
    #[cfg(feature = "v2")]
    async fn update_payment_method<'a>(
        &'a self,
        state: &SessionState,
        merchant_context: &domain::MerchantContext,
        payment_data: &mut D,
    ) {
    }

    /// This function is used to apply the 3DS authentication strategy
    async fn apply_three_ds_authentication_strategy<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _business_profile: &domain::Profile,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    /// Get connector tokenization action
    #[cfg(feature = "v2")]
    async fn get_connector_tokenization_action<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &D,
    ) -> RouterResult<(payments::TokenizationAction)> {
        Ok(payments::TokenizationAction::SkipConnectorTokenization)
    }

    // #[cfg(feature = "v2")]
    // async fn call_connector<'a, RouterDataReq>(
    //     &'a self,
    //     _state: &SessionState,
    //     _req_state: ReqState,
    //     _merchant_context: &domain::MerchantContext,
    //     _business_profile: &domain::Profile,
    //     _payment_method_data: Option<&domain::PaymentMethodData>,
    //     _connector: api::ConnectorData,
    //     _customer: &Option<domain::Customer>,
    //     _payment_data: &mut D,
    //     _call_connector_action: common_enums::CallConnectorAction,
    // ) -> CustomResult<
    //     hyperswitch_domain_models::router_data::RouterData<F, RouterDataReq, PaymentsResponseData>,
    //     errors::ApiErrorResponse,
    // > {
    //     // TODO: raise an error here
    //     todo!();
    // }
}

#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait UpdateTracker<F, D, Req>: Send {
    /// Update the tracker information with the new data from request or calculated by the operations performed after get trackers
    /// This will persist the SessionData ( PaymentData ) in the database
    ///
    /// In case we are calling a processor / connector, we persist all the data in the database and then call the connector
    async fn update_trackers<'b>(
        &'b self,
        db: &'b SessionState,
        req_state: ReqState,
        payment_data: D,
        customer: Option<domain::Customer>,
        storage_scheme: enums::MerchantStorageScheme,
        updated_customer: Option<storage::CustomerUpdate>,
        mechant_key_store: &domain::MerchantKeyStore,
        frm_suggestion: Option<FrmSuggestion>,
        header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(BoxedOperation<'b, F, Req, D>, D)>
    where
        F: 'b + Send;
}

#[cfg(feature = "v2")]
#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait CallConnector<F, D, RouterDReq: Send>: Send {
    async fn call_connector<'b>(
        &'b self,
        db: &'b SessionState,
        req_state: ReqState,
        payment_data: D,
        key_store: &domain::MerchantKeyStore,
        call_connector_action: common_enums::CallConnectorAction,
        connector_data: api::ConnectorData,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<types::RouterData<F, RouterDReq, PaymentsResponseData>>
    where
        F: 'b + Send + Sync,
        D: super::flows::ConstructFlowSpecificData<F, RouterDReq, PaymentsResponseData>,
        types::RouterData<F, RouterDReq, PaymentsResponseData>:
            super::flows::Feature<F, RouterDReq> + Send;
}

#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait PostUpdateTracker<F, D, R: Send>: Send {
    /// Update the tracker information with the response from the connector
    /// The response from routerdata is used to update paymentdata and also persist this in the database
    #[cfg(feature = "v1")]
    async fn update_tracker<'b>(
        &'b self,
        db: &'b SessionState,
        payment_data: D,
        response: types::RouterData<F, R, PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
        locale: &Option<String>,
        #[cfg(feature = "dynamic_routing")] routable_connector: Vec<RoutableConnectorChoice>,
        #[cfg(feature = "dynamic_routing")] business_profile: &domain::Profile,
    ) -> RouterResult<D>
    where
        F: 'b + Send + Sync;

    #[cfg(feature = "v2")]
    async fn update_tracker<'b>(
        &'b self,
        db: &'b SessionState,
        payment_data: D,
        response: types::RouterData<F, R, PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<D>
    where
        F: 'b + Send + Sync,
        types::RouterData<F, R, PaymentsResponseData>:
            hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<F, R, D>;

    async fn save_pm_and_mandate<'b>(
        &self,
        _state: &SessionState,
        _resp: &types::RouterData<F, R, PaymentsResponseData>,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut D,
        _business_profile: &domain::Profile,
    ) -> CustomResult<(), errors::ApiErrorResponse>
    where
        F: 'b + Clone + Send + Sync,
    {
        Ok(())
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<
        D,
        F: Clone + Send,
        Op: Send + Sync + Operation<F, api::PaymentsRetrieveRequest, Data = D>,
    > Domain<F, api::PaymentsRetrieveRequest, D> for Op
where
    for<'a> &'a Op: Operation<F, api::PaymentsRetrieveRequest, Data = D>,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send,
{
    #[instrument(skip_all)]
    #[cfg(feature = "v1")]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        _request: Option<CustomerDetails>,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsRetrieveRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        let db = &*state.store;
        let customer = match payment_data.get_payment_intent().customer_id.as_ref() {
            None => None,
            Some(customer_id) => {
                // This function is to retrieve customer details. If the customer is deleted, it returns
                // customer details that contains the fields as Redacted
                db.find_customer_optional_with_redacted_customer_details_by_customer_id_merchant_id(
                    &state.into(),
                    customer_id,
                    &merchant_key_store.merchant_id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await?
            }
        };

        if let Some(email) = customer.as_ref().and_then(|inner| inner.email.clone()) {
            payment_data.set_email_if_not_present(email.into());
        }

        Ok((Box::new(self), customer))
    }

    async fn get_connector<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        state: &SessionState,
        _request: &api::PaymentsRetrieveRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut D,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, api::PaymentsRetrieveRequest, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<D, F: Clone + Send, Op: Send + Sync + Operation<F, api::PaymentsCaptureRequest, Data = D>>
    Domain<F, api::PaymentsCaptureRequest, D> for Op
where
    for<'a> &'a Op: Operation<F, api::PaymentsCaptureRequest, Data = D>,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send,
{
    #[instrument(skip_all)]
    #[cfg(feature = "v1")]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        _request: Option<CustomerDetails>,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsCaptureRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        let db = &*state.store;

        let customer = match payment_data.get_payment_intent().customer_id.as_ref() {
            None => None,
            Some(customer_id) => {
                db.find_customer_optional_by_customer_id_merchant_id(
                    &state.into(),
                    customer_id,
                    &merchant_key_store.merchant_id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await?
            }
        };

        if let Some(email) = customer.as_ref().and_then(|inner| inner.email.clone()) {
            payment_data.set_email_if_not_present(email.into());
        }

        Ok((Box::new(self), customer))
    }

    #[instrument(skip_all)]
    #[cfg(feature = "v2")]
    async fn get_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsCaptureRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        todo!()
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut D,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, api::PaymentsCaptureRequest, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    async fn get_connector<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        state: &SessionState,
        _request: &api::PaymentsCaptureRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<D, F: Clone + Send, Op: Send + Sync + Operation<F, api::PaymentsCancelRequest, Data = D>>
    Domain<F, api::PaymentsCancelRequest, D> for Op
where
    for<'a> &'a Op: Operation<F, api::PaymentsCancelRequest, Data = D>,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send,
{
    #[instrument(skip_all)]
    #[cfg(feature = "v1")]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        _request: Option<CustomerDetails>,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsCancelRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        let db = &*state.store;

        let customer = match payment_data.get_payment_intent().customer_id.as_ref() {
            None => None,
            Some(customer_id) => {
                db.find_customer_optional_by_customer_id_merchant_id(
                    &state.into(),
                    customer_id,
                    &merchant_key_store.merchant_id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await?
            }
        };

        if let Some(email) = customer.as_ref().and_then(|inner| inner.email.clone()) {
            payment_data.set_email_if_not_present(email.into());
        }

        Ok((Box::new(self), customer))
    }

    #[instrument(skip_all)]
    #[cfg(feature = "v2")]
    async fn get_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsCancelRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        todo!()
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut D,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, api::PaymentsCancelRequest, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    async fn get_connector<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        state: &SessionState,
        _request: &api::PaymentsCancelRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<D, F: Clone + Send, Op: Send + Sync + Operation<F, api::PaymentsRejectRequest, Data = D>>
    Domain<F, api::PaymentsRejectRequest, D> for Op
where
    for<'a> &'a Op: Operation<F, api::PaymentsRejectRequest, Data = D>,
{
    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _request: Option<CustomerDetails>,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsRejectRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        Ok((Box::new(self), None))
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn get_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsRejectRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        Ok((Box::new(self), None))
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut D,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, api::PaymentsRejectRequest, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    async fn get_connector<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        state: &SessionState,
        _request: &api::PaymentsRejectRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

/// Validate if a particular operation can be performed for the given intent status
pub trait ValidateStatusForOperation {
    fn validate_status_for_operation(
        &self,
        intent_status: common_enums::IntentStatus,
    ) -> Result<(), errors::ApiErrorResponse>;
}

/// Should the connector be called for this operation
pub trait ShouldCallConnector {
    fn should_call_connector(
        &self,
        intent_status: common_enums::IntentStatus,
        force_sync: Option<bool>,
    ) -> bool;
}


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

pub struct NomupayErrorResponse


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

pub struct BusinessGenericLinkConfig


// Struct: GetRoleRequest
// File: crates/api_models/src/user_role/role.rs
// Module: api_models
// Implementations: 0

pub struct GetRoleRequest


// Implementation: impl Capturable for for PaymentsSyncData
// File: crates/router/src/types.rs
// Module: router
// Methods: 4 total (0 public)

impl Capturable for for PaymentsSyncData


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

impl api::PaymentToken for for Globalpay


// Implementation: impl StaticRoutingAlgorithm
// File: crates/api_models/src/routing.rs
// Module: api_models
// Methods: 1 total (1 public)

impl StaticRoutingAlgorithm


// Struct: FortePaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/forte/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FortePaymentsRequest


// Function: get_decoupled_authentication_type
// File: crates/hyperswitch_domain_models/src/router_request_types/authentication.rs
// Module: hyperswitch_domain_models

pub fn get_decoupled_authentication_type(&self) -> common_enums::DecoupledAuthenticationType


// Struct: ElavonPaymentsResponse
// File: crates/hyperswitch_connectors/src/connectors/elavon/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ElavonPaymentsResponse


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

pub struct BokuPsyncRequest


// Struct: NovalnetWebhookNotificationResponse
// File: crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NovalnetWebhookNotificationResponse


// Struct: ApplePayWalletData
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Implementations: 1

pub struct ApplePayWalletData


// Function: get_id
// File: crates/hyperswitch_domain_models/src/merchant_account.rs
// Module: hyperswitch_domain_models

// Documentation: Get the unique identifier of MerchantAccount

pub fn get_id(&self) -> &common_utils::id_type::MerchantId


// Function: get_expired_page
// File: crates/router/src/routes/dummy_connector/utils.rs
// Module: router

pub fn get_expired_page(dummy_connector_conf: &settings::DummyConnector) -> String


// Struct: PaymentsMandateReferenceRecord
// File: crates/hyperswitch_domain_models/src/mandates.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PaymentsMandateReferenceRecord


// Struct: DebtorInformation
// File: crates/hyperswitch_connectors/src/connectors/trustpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DebtorInformation


// Struct: Blocklist
// File: crates/router/src/routes/app.rs
// Module: router
// Implementations: 1

pub struct Blocklist


        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let aci_notification: aci::AciWebhookNotification =
            serde_json::from_slice(request.body)
                .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)
                .attach_printable("Failed to deserialize ACI webhook notification for resource object (expected decrypted payload)")?;

        match aci_notification.event_type {
            aci::AciWebhookEventType::Payment => {
                let payment_payload: aci::AciPaymentWebhookPayload =
                    serde_json::from_value(aci_notification.payload)
                        .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)
                        .attach_printable("Failed to deserialize ACI payment webhook payload")?;
                Ok(Box::new(payment_payload))
            }
        }
    }
}

static ACI_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> = LazyLock::new(|| {
    let supported_capture_methods = vec![
        enums::CaptureMethod::Automatic,
        enums::CaptureMethod::Manual,
    ];

    let supported_card_networks = vec![
        common_enums::CardNetwork::Visa,
        common_enums::CardNetwork::Mastercard,
        common_enums::CardNetwork::AmericanExpress,
        common_enums::CardNetwork::JCB,
        common_enums::CardNetwork::DinersClub,
        common_enums::CardNetwork::Discover,
        common_enums::CardNetwork::UnionPay,
        common_enums::CardNetwork::Maestro,
    ];

    let mut aci_supported_payment_methods = SupportedPaymentMethods::new();

    aci_supported_payment_methods.add(
        enums::PaymentMethod::Wallet,
        enums::PaymentMethodType::MbWay,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );

    aci_supported_payment_methods.add(
        enums::PaymentMethod::Wallet,
        enums::PaymentMethodType::AliPay,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );

    aci_supported_payment_methods.add(
        enums::PaymentMethod::Card,
        enums::PaymentMethodType::Credit,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::Supported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: Some(
                api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                    api_models::feature_matrix::CardSpecificFeatures {
                        three_ds: common_enums::FeatureStatus::Supported,
                        no_three_ds: common_enums::FeatureStatus::Supported,
                        supported_card_networks: supported_card_networks.clone(),
                    }
                }),
            ),
        },
    );

    aci_supported_payment_methods.add(
        enums::PaymentMethod::Card,
        enums::PaymentMethodType::Debit,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::Supported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: Some(
                api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                    api_models::feature_matrix::CardSpecificFeatures {
                        three_ds: common_enums::FeatureStatus::Supported,
                        no_three_ds: common_enums::FeatureStatus::Supported,
                        supported_card_networks: supported_card_networks.clone(),
                    }
                }),
            ),
        },
    );

    aci_supported_payment_methods.add(
        enums::PaymentMethod::BankRedirect,
        enums::PaymentMethodType::Eps,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );
    aci_supported_payment_methods.add(
        enums::PaymentMethod::BankRedirect,
        enums::PaymentMethodType::Eft,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );
    aci_supported_payment_methods.add(
        enums::PaymentMethod::BankRedirect,
        enums::PaymentMethodType::Ideal,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );
    aci_supported_payment_methods.add(
        enums::PaymentMethod::BankRedirect,
        enums::PaymentMethodType::Giropay,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );
    aci_supported_payment_methods.add(
        enums::PaymentMethod::BankRedirect,
        enums::PaymentMethodType::Sofort,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );
    aci_supported_payment_methods.add(
        enums::PaymentMethod::BankRedirect,
        enums::PaymentMethodType::Interac,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );
    aci_supported_payment_methods.add(
        enums::PaymentMethod::BankRedirect,
        enums::PaymentMethodType::Przelewy24,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );
    aci_supported_payment_methods.add(
        enums::PaymentMethod::BankRedirect,
        enums::PaymentMethodType::Trustly,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );

    aci_supported_payment_methods.add(
        enums::PaymentMethod::PayLater,
        enums::PaymentMethodType::Klarna,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );

    aci_supported_payment_methods
});

static ACI_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "ACI",
    description:
        "ACI Payments delivers secure, real-time electronic payment solutions for businesses, banks, and governments, enabling seamless transactions across channels.",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Beta,
};

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

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

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

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