File: crates/common_utils/src/crypto.rs
 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 =


pub struct PowertranzAddressDetails {
//     first_name: Option<Secret<String>>,
//     last_name: Option<Secret<String>>,
//     line1: Option<Secret<String>>,
//     line2: Option<Secret<String>>,
//     city: Option<String>,
//     country: Option<enums::CountryAlpha2>,
//     state: Option<Secret<String>>,
//     postal_code: Option<Secret<String>>,
//     email_address: Option<Email>,
//     phone_number: Option<Secret<String>>,
// }


OpenAPI Block Path: components.schemas.AchBankTransfer
{
  "type": "object",
  "required": [
    "bank_account_number",
    "bank_routing_number"
  ],
  "properties": {
    "bank_name": {
      "type": "string",
      "description": "Bank name",
      "example": "Deutsche Bank",
      "nullable": true
    },
    "bank_country_code": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CountryAlpha2"
        }
      ],
      "nullable": true
    },
    "bank_city": {
      "type": "string",
      "description": "Bank city",
      "example": "California",
      "nullable": true
    },
    "bank_account_number": {
      "type": "string",
      "description": "Bank account number is an unique identifier assigned by a bank to a customer.",
      "example": "000123456"
    },
    "bank_routing_number": {
      "type": "string",
      "description": "[9 digits] Routing number - used in USA for identifying a specific bank.",
      "example": "110000000"
    }
  }
}


File: crates/openapi/src/openapi_v2.rs

use crate::routes;

#[derive(utoipa::OpenApi)]
#[openapi(
    info(
        title = "Hyperswitch - API Documentation",
        contact(
            name = "Hyperswitch Support",
            url = "https://hyperswitch.io",
            email = "hyperswitch@juspay.in"
        ),
        // terms_of_service = "https://www.juspay.io/terms",
        description = r#"
## Get started

Hyperswitch provides a collection of APIs that enable you to process and manage payments.
Our APIs accept and return JSON in the HTTP body, and return standard HTTP response codes.

You can consume the APIs directly using your favorite HTTP/REST library.

We have a testing environment referred to "sandbox", which you can setup to test API calls without
affecting production data.
Currently, our sandbox environment is live while our production environment is under development
and will be available soon.
You can sign up on our Dashboard to get API keys to access Hyperswitch API.

### Environment

Use the following base URLs when making requests to the APIs:

| Environment   |  Base URL                          |
|---------------|------------------------------------|
| Sandbox       | <https://sandbox.hyperswitch.io>   |
| Production    | <https://api.hyperswitch.io>       |

## Authentication

When you sign up on our [dashboard](https://app.hyperswitch.io) and create a merchant
account, you are given a secret key (also referred as api-key) and a publishable key.
You may authenticate all API requests with Hyperswitch server by providing the appropriate key in
the request Authorization header.

| Key             |  Description                                                                                |
|-----------------|-----------------------------------------------------------------------------------------------|
| api-key         | Private key. Used to authenticate all API requests from your merchant server                  |
| publishable key | Unique identifier for your account. Used to authenticate API requests from your app's client  |

Never share your secret api keys. Keep them guarded and secure.
"#,
    ),
    servers(
        (url = "https://sandbox.hyperswitch.io", description = "Sandbox Environment")
    ),
    tags(
        (name = "Merchant Account", description = "Create and manage merchant accounts"),
        (name = "Profile", description = "Create and manage profiles"),
        (name = "Merchant Connector Account", description = "Create and manage merchant connector accounts"),
        (name = "Payments", description = "Create and manage one-time payments, recurring payments and mandates"),
        (name = "Refunds", description = "Create and manage refunds for successful payments"),
        (name = "Mandates", description = "Manage mandates"),
        (name = "Customers", description = "Create and manage customers"),
        (name = "Payment Methods", description = "Create and manage payment methods of customers"),
        (name = "Disputes", description = "Manage disputes"),
        (name = "API Key", description = "Create and manage API Keys"),
        (name = "Payouts", description = "Create and manage payouts"),
        (name = "payment link", description = "Create payment link"),
        (name = "Routing", description = "Create and manage routing configurations"),
        (name = "Event", description = "Manage events"),
    ),
    // The paths will be displayed in the same order as they are registered here
    paths(
        // Routes for Organization
        routes::organization::organization_create,
        routes::organization::organization_retrieve,
        routes::organization::organization_update,
        routes::organization::merchant_account_list,

        // Routes for merchant connector account
        routes::merchant_connector_account::connector_create,
        routes::merchant_connector_account::connector_retrieve,
        routes::merchant_connector_account::connector_update,
        routes::merchant_connector_account::connector_delete,

        // Routes for merchant account
        routes::merchant_account::merchant_account_create,
        routes::merchant_account::merchant_account_retrieve,
        routes::merchant_account::merchant_account_update,
        routes::merchant_account::profiles_list,

        // Routes for profile
        routes::profile::profile_create,
        routes::profile::profile_retrieve,
        routes::profile::profile_update,
        routes::profile::connector_list,

        // Routes for routing under profile
        routes::profile::routing_link_config,
        routes::profile::routing_unlink_config,
        routes::profile::routing_update_default_config,
        routes::profile::routing_retrieve_default_config,
        routes::profile::routing_retrieve_linked_config,

        // Routes for routing
        routes::routing::routing_create_config,
        routes::routing::routing_retrieve_config,

        // Routes for api keys
        routes::api_keys::api_key_create,
        routes::api_keys::api_key_retrieve,
        routes::api_keys::api_key_update,
        routes::api_keys::api_key_revoke,
        routes::api_keys::api_key_list,

        //Routes for customers
        routes::customers::customers_create,
        routes::customers::customers_retrieve,
        routes::customers::customers_update,
        routes::customers::customers_delete,
        routes::customers::customers_list,

        //Routes for payments
        routes::payments::payments_create_intent,
        routes::payments::payments_get_intent,
        routes::payments::payments_update_intent,
        routes::payments::payments_confirm_intent,
        routes::payments::payment_status,
        routes::payments::payments_create_and_confirm_intent,
        routes::payments::payments_connector_session,
        routes::payments::list_payment_methods,
        routes::payments::payments_list,

        //Routes for payment methods
        routes::payment_method::create_payment_method_api,
        routes::payment_method::create_payment_method_intent_api,
        routes::payment_method::confirm_payment_method_intent_api,
        routes::payment_method::payment_method_update_api,
        routes::payment_method::payment_method_retrieve_api,
        routes::payment_method::payment_method_delete_api,
        routes::payment_method::list_customer_payment_method_api,

        //Routes for payment method session
        routes::payment_method::payment_method_session_create,
        routes::payment_method::payment_method_session_retrieve,
        routes::payment_method::payment_method_session_list_payment_methods,
        routes::payment_method::payment_method_session_update_saved_payment_method,
        routes::payment_method::payment_method_session_delete_saved_payment_method,
        routes::payment_method::payment_method_session_confirm,

        //Routes for refunds
        routes::refunds::refunds_create,
        routes::refunds::refunds_metadata_update,
        routes::refunds::refunds_retrieve,
        routes::refunds::refunds_list,

        // Routes for Revenue Recovery flow under Process Tracker
        routes::revenue_recovery::revenue_recovery_pt_retrieve_api,

        // Routes for proxy
        routes::proxy::proxy_core,

        // Route for tokenization
        routes::tokenization::create_token_vault_api,
        routes::tokenization::delete_tokenized_data_api,
    ),
    components(schemas(
        common_utils::types::MinorUnit,
        common_utils::types::StringMinorUnit,
        common_utils::types::TimeRange,
        common_utils::types::BrowserInformation,
        common_utils::link_utils::GenericLinkUiConfig,
        common_utils::link_utils::EnabledPaymentMethod,
        common_utils::payout_method_utils::AdditionalPayoutMethodData,
        common_utils::payout_method_utils::CardAdditionalData,
        common_utils::payout_method_utils::BankAdditionalData,
        common_utils::payout_method_utils::WalletAdditionalData,
        common_utils::payout_method_utils::AchBankTransferAdditionalData,
        common_utils::payout_method_utils::BacsBankTransferAdditionalData,
        common_utils::payout_method_utils::SepaBankTransferAdditionalData,
        common_utils::payout_method_utils::PixBankTransferAdditionalData,
        common_utils::payout_method_utils::PaypalAdditionalData,
        common_utils::payout_method_utils::VenmoAdditionalData,
        common_types::payments::SplitPaymentsRequest,
        common_types::payments::GpayTokenizationData,
        common_types::payments::GPayPredecryptData,
        common_types::payments::GpayEcryptedTokenizationData,
        common_types::payments::ApplePayPaymentData,
        common_types::payments::ApplePayPredecryptData,
        common_types::payments::ApplePayCryptogramData,
        common_types::payments::ApplePayPaymentData,
        common_types::payments::StripeSplitPaymentRequest,
        common_types::domain::AdyenSplitData,
        common_types::payments::AcceptanceType,
        common_types::payments::CustomerAcceptance,
        common_types::payments::OnlineMandate,
        common_types::payments::XenditSplitRequest,
        common_types::payments::XenditSplitRoute,
        common_types::payments::XenditChargeResponseData,
        common_types::payments::XenditMultipleSplitResponse,
        common_types::payments::XenditMultipleSplitRequest,
        common_types::domain::XenditSplitSubMerchantData,
        common_types::domain::AdyenSplitItem,
        common_types::domain::MerchantConnectorAuthDetails,
        common_types::refunds::StripeSplitRefundRequest,
        common_utils::types::ChargeRefunds,
        common_types::payment_methods::PaymentMethodsEnabled,
        common_types::payment_methods::PspTokenization,
        common_types::payment_methods::NetworkTokenization,
        common_types::refunds::SplitRefund,
        common_types::payments::ConnectorChargeResponseData,
        common_types::payments::StripeChargeResponseData,
        common_types::three_ds_decision_rule_engine::ThreeDSDecisionRule,
        common_types::three_ds_decision_rule_engine::ThreeDSDecision,
        common_types::payments::MerchantCountryCode,
        common_utils::request::Method,
        api_models::errors::types::GenericErrorResponseOpenApi,
        api_models::refunds::RefundsCreateRequest,
        api_models::refunds::RefundErrorDetails,
        api_models::refunds::RefundType,
        api_models::refunds::RefundResponse,
        api_models::refunds::RefundStatus,
        api_models::refunds::RefundMetadataUpdateRequest,
        api_models::organization::OrganizationCreateRequest,
        api_models::organization::OrganizationUpdateRequest,
        api_models::organization::OrganizationResponse,
        api_models::admin::MerchantAccountCreateWithoutOrgId,
        api_models::admin::MerchantAccountUpdate,
        api_models::admin::MerchantAccountDeleteResponse,
        api_models::admin::MerchantConnectorDeleteResponse,
        api_models::admin::MerchantConnectorResponse,
        api_models::admin::MerchantConnectorListResponse,
        api_models::admin::AuthenticationConnectorDetails,
        api_models::admin::ExternalVaultConnectorDetails,
        api_models::admin::ExtendedCardInfoConfig,
        api_models::admin::BusinessGenericLinkConfig,
        api_models::admin::BusinessCollectLinkConfig,
        api_models::admin::BusinessPayoutLinkConfig,
        api_models::admin::MerchantConnectorAccountFeatureMetadata,
        api_models::admin::RevenueRecoveryMetadata,
        api_models::customers::CustomerRequest,
        api_models::customers::CustomerUpdateRequest,
        api_models::customers::CustomerDeleteResponse,
        api_models::ephemeral_key::ResourceId,
        api_models::payment_methods::PaymentMethodCreate,
        api_models::payment_methods::PaymentMethodIntentCreate,
        api_models::payment_methods::PaymentMethodIntentConfirm,
        api_models::payment_methods::AuthenticationDetails,
        api_models::payment_methods::PaymentMethodResponse,
        api_models::payment_methods::PaymentMethodResponseData,
        api_models::payment_methods::CustomerPaymentMethodResponseItem,
        api_models::payment_methods::PaymentMethodResponseItem,
        api_models::payment_methods::ListMethodsForPaymentMethodsRequest,
        api_models::payment_methods::PaymentMethodListResponseForSession,
        api_models::payment_methods::CustomerPaymentMethodsListResponse,
        api_models::payment_methods::ResponsePaymentMethodsEnabled,
        api_models::payment_methods::PaymentMethodSubtypeSpecificData,
        api_models::payment_methods::ResponsePaymentMethodTypes,
        api_models::payment_methods::PaymentExperienceTypes,
        api_models::payment_methods::CardNetworkTypes,
        api_models::payment_methods::BankDebitTypes,
        api_models::payment_methods::BankTransferTypes,
        api_models::payment_methods::PaymentMethodDeleteResponse,
        api_models::payment_methods::PaymentMethodUpdate,
        api_models::payment_methods::PaymentMethodUpdateData,
        api_models::payment_methods::CardDetailFromLocker,
        api_models::payment_methods::PaymentMethodCreateData,
        api_models::payment_methods::ProxyCardDetails,
        api_models::payment_methods::CardDetail,
        api_models::payment_methods::CardDetailUpdate,
        api_models::payment_methods::CardType,
        api_models::payment_methods::RequestPaymentMethodTypes,
        api_models::payment_methods::CardType,
        api_models::payment_methods::PaymentMethodListData,
        api_models::poll::PollResponse,
        api_models::poll::PollStatus,
        api_models::customers::CustomerResponse,
        api_models::admin::AcceptedCountries,
        api_models::admin::AcceptedCurrencies,
        api_models::enums::AdyenSplitType,
        api_models::enums::ProductType,
        api_models::enums::PaymentType,
        api_models::enums::ScaExemptionType,
        api_models::enums::PaymentMethod,
        api_models::enums::PaymentMethodType,
        api_models::enums::ConnectorType,
        api_models::enums::PayoutConnectors,
        api_models::enums::AuthenticationConnectors,
        api_models::enums::VaultSdk,
        api_models::enums::Currency,
        api_models::enums::DocumentKind,
        api_models::enums::IntentStatus,
        api_models::enums::CaptureMethod,
        api_models::enums::FutureUsage,
        api_models::enums::AuthenticationType,
        api_models::enums::Connector,
        api_models::enums::PaymentMethod,
        api_models::enums::PaymentMethodIssuerCode,
        api_models::enums::MandateStatus,
        api_models::enums::MerchantProductType,
        api_models::enums::PaymentExperience,
        api_models::enums::BankNames,
        api_models::enums::BankType,
        api_models::enums::BankHolderType,
        api_models::enums::CardNetwork,
        api_models::enums::MerchantCategoryCode,
        api_models::enums::TokenDataType,
        api_models::enums::DisputeStage,
        api_models::enums::DisputeStatus,
        api_models::enums::CountryAlpha2,
        api_models::enums::CountryAlpha3,
        api_models::enums::FieldType,
        api_models::enums::FrmAction,
        api_models::enums::FrmPreferredFlowTypes,
        api_models::enums::RetryAction,
        api_models::enums::AttemptStatus,
        api_models::enums::CaptureStatus,
        api_models::enums::ReconStatus,
        api_models::enums::ConnectorStatus,
        api_models::enums::AuthorizationStatus,
        api_models::enums::ElementPosition,
        api_models::enums::ElementSize,
        api_models::enums::TaxStatus,
        api_models::enums::SizeVariants,
        api_models::enums::MerchantProductType,
        api_models::enums::PaymentLinkDetailsLayout,
        api_models::enums::PaymentMethodStatus,
        api_models::enums::HyperswitchConnectorCategory,
        api_models::enums::ConnectorIntegrationStatus,
        api_models::enums::FeatureStatus,
        api_models::enums::OrderFulfillmentTimeOrigin,
        api_models::enums::UIWidgetFormLayout,
        api_models::enums::MerchantProductType,
        api_models::enums::CtpServiceProvider,
        api_models::enums::PaymentLinkSdkLabelType,
        api_models::enums::PaymentLinkShowSdkTerms,
        api_models::enums::OrganizationType,
        api_models::admin::MerchantConnectorCreate,
        api_models::admin::AdditionalMerchantData,
        api_models::admin::CardTestingGuardConfig,
        api_models::admin::CardTestingGuardStatus,
        api_models::admin::ConnectorWalletDetails,
        api_models::admin::MerchantRecipientData,
        api_models::admin::MerchantAccountData,
        api_models::admin::MerchantConnectorUpdate,
        api_models::admin::PrimaryBusinessDetails,
        api_models::admin::FrmConfigs,
        api_models::admin::FrmPaymentMethod,
        api_models::admin::FrmPaymentMethodType,
        api_models::admin::MerchantConnectorDetailsWrap,
        api_models::admin::MerchantConnectorDetails,
        api_models::admin::MerchantConnectorWebhookDetails,
        api_models::admin::ProfileCreate,
        api_models::admin::ProfileResponse,
        api_models::admin::BusinessPaymentLinkConfig,
        api_models::admin::PaymentLinkBackgroundImageConfig,
        api_models::admin::PaymentLinkConfigRequest,
        api_models::admin::PaymentLinkConfig,
        api_models::admin::PaymentLinkTransactionDetails,
        api_models::admin::TransactionDetailsUiConfiguration,
        api_models::disputes::DisputeResponse,
        api_models::disputes::DisputeResponsePaymentsRetrieve,
        api_models::gsm::GsmCreateRequest,
        api_models::gsm::GsmRetrieveRequest,
        api_models::gsm::GsmUpdateRequest,
        api_models::gsm::GsmDeleteRequest,
        api_models::gsm::GsmDeleteResponse,
        api_models::gsm::GsmResponse,
        api_models::enums::GsmDecision,
        api_models::enums::GsmFeature,
        common_types::domain::GsmFeatureData,
        common_types::domain::RetryFeatureData,
        api_models::payments::NullObject,
        api_models::payments::AddressDetails,
        api_models::payments::BankDebitData,
        api_models::payments::AliPayQr,
        api_models::payments::PaymentAttemptFeatureMetadata,
        api_models::payments::PaymentAttemptRevenueRecoveryData,
        api_models::payments::BillingConnectorPaymentMethodDetails,
        api_models::payments::RecordAttemptErrorDetails,
        api_models::payments::BillingConnectorAdditionalCardInfo,
        api_models::payments::AliPayRedirection,
        api_models::payments::MomoRedirection,
        api_models::payments::TouchNGoRedirection,
        api_models::payments::GcashRedirection,
        api_models::payments::KakaoPayRedirection,
        api_models::payments::AliPayHkRedirection,
        api_models::payments::GoPayRedirection,
        api_models::payments::MbWayRedirection,
        api_models::payments::MobilePayRedirection,
        api_models::payments::WeChatPayRedirection,
        api_models::payments::WeChatPayQr,
        api_models::payments::BankDebitBilling,
        api_models::payments::CryptoData,
        api_models::payments::RewardData,
        api_models::payments::UpiData,
        api_models::payments::UpiCollectData,
        api_models::payments::UpiIntentData,
        api_models::payments::VoucherData,
        api_models::payments::BoletoVoucherData,
        api_models::payments::AlfamartVoucherData,
        api_models::payments::IndomaretVoucherData,
        api_models::payments::Address,
        api_models::payments::VoucherData,
        api_models::payments::JCSVoucherData,
        api_models::payments::AlfamartVoucherData,
        api_models::payments::IndomaretVoucherData,
        api_models::payments::BankRedirectData,
        api_models::payments::RealTimePaymentData,
        api_models::payments::BankRedirectBilling,
        api_models::payments::BankRedirectBilling,
        api_models::payments::ConnectorMetadata,
        api_models::payments::FeatureMetadata,
        api_models::payments::SdkType,
        api_models::payments::ApplepayConnectorMetadataRequest,
        api_models::payments::SessionTokenInfo,
        api_models::payments::PaymentProcessingDetailsAt,
        api_models::payments::ApplepayInitiative,
        api_models::payments::PaymentProcessingDetails,
        api_models::payments::PaymentMethodDataResponseWithBilling,
        api_models::payments::PaymentMethodDataResponse,
        api_models::payments::CardResponse,
        api_models::payments::PaylaterResponse,
        api_models::payments::KlarnaSdkPaymentMethodResponse,
        api_models::payments::SwishQrData,
        api_models::payments::RevolutPayData,
        api_models::payments::AirwallexData,
        api_models::payments::BraintreeData,
        api_models::payments::NoonData,
        api_models::payments::OrderDetailsWithAmount,
        api_models::payments::NextActionType,
        api_models::payments::WalletData,
        api_models::payments::NextActionData,
        api_models::payments::PayLaterData,
        api_models::payments::MandateData,
        api_models::payments::PhoneDetails,
        api_models::payments::PaymentMethodData,
        api_models::payments::PaymentMethodDataRequest,
        api_models::payments::MandateType,
        api_models::payments::MandateAmountData,
        api_models::payments::Card,
        api_models::payments::CardRedirectData,
        api_models::payments::CardToken,
        api_models::payments::ConnectorTokenDetails,
        api_models::payments::PaymentsRequest,
        api_models::payments::PaymentsResponse,
        api_models::payments::PaymentsListResponseItem,
        api_models::payments::PaymentsRetrieveRequest,
        api_models::payments::PaymentsStatusRequest,
        api_models::payments::PaymentsCaptureRequest,
        api_models::payments::PaymentsSessionRequest,
        api_models::payments::PaymentsSessionResponse,
        api_models::payments::PaymentsCreateIntentRequest,
        api_models::payments::PaymentsUpdateIntentRequest,
        api_models::payments::PaymentsIntentResponse,
        api_models::payments::PaymentAttemptListRequest,
        api_models::payments::PaymentAttemptListResponse,
        api_models::payments::PazeWalletData,
        api_models::payments::AmountDetails,
        api_models::payments::AmountDetailsUpdate,
        api_models::payments::SessionToken,
        api_models::payments::VaultSessionDetails,
        api_models::payments::VgsSessionDetails,
        api_models::payments::HyperswitchVaultSessionDetails,
        api_models::payments::ApplePaySessionResponse,
        api_models::payments::ThirdPartySdkSessionResponse,
        api_models::payments::NoThirdPartySdkSessionResponse,
        api_models::payments::SecretInfoToInitiateSdk,
        api_models::payments::ApplePayPaymentRequest,
        api_models::payments::ApplePayBillingContactFields,
        api_models::payments::ApplePayShippingContactFields,
        api_models::payments::ApplePayAddressParameters,
        api_models::payments::ApplePayRecurringPaymentRequest,
        api_models::payments::ApplePayRegularBillingRequest,
        api_models::payments::ApplePayPaymentTiming,
        api_models::payments::RecurringPaymentIntervalUnit,
        api_models::payments::ApplePayRecurringDetails,
        api_models::payments::ApplePayRegularBillingDetails,
        api_models::payments::AmountInfo,
        api_models::payments::GooglePayWalletData,
        api_models::payments::PayPalWalletData,
        api_models::payments::PaypalRedirection,
        api_models::payments::GpayMerchantInfo,
        api_models::payments::GpayAllowedPaymentMethods,
        api_models::payments::GpayAllowedMethodsParameters,
        api_models::payments::GpayTokenizationSpecification,
        api_models::payments::GpayTokenParameters,
        api_models::payments::GpayTransactionInfo,
        api_models::payments::GpaySessionTokenResponse,
        api_models::payments::GooglePayThirdPartySdkData,
        api_models::payments::KlarnaSessionTokenResponse,
        api_models::payments::PaypalSessionTokenResponse,
        api_models::payments::ApplepaySessionTokenResponse,
        api_models::payments::SdkNextAction,
        api_models::payments::NextActionCall,
        api_models::payments::SdkNextActionData,
        api_models::payments::SamsungPayWalletData,
        api_models::payments::WeChatPay,
        api_models::payments::GooglePayPaymentMethodInfo,
        api_models::payments::ApplePayWalletData,
        api_models::payments::ApplepayPaymentMethod,
        api_models::payments::PazeSessionTokenResponse,
        api_models::payments::SamsungPaySessionTokenResponse,
        api_models::payments::SamsungPayMerchantPaymentInformation,
        api_models::payments::SamsungPayAmountDetails,
        api_models::payments::SamsungPayAmountFormat,
        api_models::payments::SamsungPayProtocolType,
        api_models::payments::SamsungPayWalletCredentials,
        api_models::payments::SamsungPayWebWalletData,
        api_models::payments::SamsungPayAppWalletData,
        api_models::payments::SamsungPayCardBrand,
        api_models::payments::SamsungPayTokenData,
        api_models::payments::PaymentsCancelRequest,
        api_models::payments::PaymentListResponse,
        api_models::payments::CashappQr,
        api_models::payments::BankTransferData,
        api_models::payments::BankTransferNextStepsData,
        api_models::payments::SepaAndBacsBillingDetails,
        api_models::payments::AchBillingDetails,
        api_models::payments::MultibancoBillingDetails,
        api_models::payments::DokuBillingDetails,
        api_models::payments::BankTransferInstructions,
        api_models::payments::MobilePaymentNextStepData,
        api_models::payments::MobilePaymentConsent,
        api_models::payments::IframeData,
        api_models::payments::ReceiverDetails,
        api_models::payments::AchTransfer,
        api_models::payments::MultibancoTransferInstructions,
        api_models::payments::DokuBankTransferInstructions,
        api_models::payments::AmazonPayRedirectData,
        api_models::payments::SkrillData,
        api_models::payments::PayseraData,
        api_models::payments::ApplePayRedirectData,
        api_models::payments::ApplePayThirdPartySdkData,
        api_models::payments::GooglePayRedirectData,
        api_models::payments::GooglePayThirdPartySdk,
        api_models::mandates::NetworkTransactionIdAndCardDetails,
        api_models::payments::GooglePaySessionResponse,
        api_models::payments::GpayShippingAddressParameters,
        api_models::payments::GpayBillingAddressParameters,
        api_models::payments::GpayBillingAddressFormat,
        api_models::payments::SepaBankTransferInstructions,
        api_models::payments::BacsBankTransferInstructions,
        api_models::payments::RedirectResponse,
        api_models::payments::RequestSurchargeDetails,
        api_models::payments::PaymentRevenueRecoveryMetadata,
        api_models::payments::BillingConnectorPaymentDetails,
        api_models::enums::PaymentConnectorTransmission,
        api_models::enums::TriggeredBy,
        api_models::payments::PaymentAttemptResponse,
        api_models::payments::PaymentAttemptRecordResponse,
        api_models::payments::PaymentAttemptAmountDetails,
        api_models::payments::CaptureResponse,
        api_models::payments::PaymentsIncrementalAuthorizationRequest,
        api_models::payments::IncrementalAuthorizationResponse,
        api_models::payments::PaymentsCompleteAuthorizeRequest,
        api_models::payments::PaymentsExternalAuthenticationRequest,
        api_models::payments::PaymentsExternalAuthenticationResponse,
        api_models::payments::SdkInformation,
        api_models::payments::DeviceChannel,
        api_models::payments::ThreeDsCompletionIndicator,
        api_models::payments::MifinityData,
        api_models::payments::ClickToPaySessionResponse,
        api_models::enums::TransactionStatus,
        api_models::payments::PaymentCreatePaymentLinkConfig,
        api_models::payments::ThreeDsData,
        api_models::payments::ThreeDsMethodData,
        api_models::payments::PollConfigResponse,
        api_models::payments::PollConfig,
        api_models::payments::ExternalAuthenticationDetailsResponse,
        api_models::payments::ExtendedCardInfo,
        api_models::payments::PaymentsConfirmIntentRequest,
        api_models::payments::AmountDetailsResponse,
        api_models::payments::BankCodeResponse,
        api_models::payments::Order,
        api_models::payments::SortOn,
        api_models::payments::SortBy,
        api_models::payments::PaymentMethodListResponseForPayments,
        api_models::payments::ResponsePaymentMethodTypesForPayments,
        api_models::payment_methods::CardNetworkTokenizeRequest,
        api_models::payment_methods::CardNetworkTokenizeResponse,
        api_models::payment_methods::CardType,
        api_models::payments::AmazonPaySessionTokenData,
        api_models::payments::AmazonPayMerchantCredentials,
        api_models::payments::AmazonPayWalletData,
        api_models::payments::AmazonPaySessionTokenResponse,
        api_models::payments::AmazonPayPaymentIntent,
        api_models::payments::AmazonPayDeliveryOptions,
        api_models::payments::AmazonPayDeliveryPrice,
        api_models::payments::AmazonPayShippingMethod,
        api_models::payment_methods::RequiredFieldInfo,
        api_models::payment_methods::MaskedBankDetails,
        api_models::payment_methods::SurchargeDetailsResponse,
        api_models::payment_methods::SurchargeResponse,
        api_models::payment_methods::SurchargePercentage,
        api_models::payment_methods::PaymentMethodCollectLinkRequest,
        api_models::payment_methods::PaymentMethodCollectLinkResponse,
        api_models::payment_methods::PaymentMethodSubtypeSpecificData,
        api_models::payment_methods::PaymentMethodSessionRequest,
        api_models::payment_methods::PaymentMethodSessionResponse,
        api_models::payment_methods::PaymentMethodsSessionUpdateRequest,
        api_models::payment_methods::NetworkTokenResponse,
        api_models::payment_methods::NetworkTokenDetailsPaymentMethod,
        api_models::payment_methods::NetworkTokenDetailsResponse,
        api_models::payment_methods::TokenDataResponse,
        api_models::payment_methods::TokenDetailsResponse,
        api_models::payment_methods::TokenizeCardRequest,
        api_models::payment_methods::TokenizeDataRequest,
        api_models::payment_methods::TokenizePaymentMethodRequest,
        api_models::refunds::RefundListRequest,
        api_models::refunds::RefundListResponse,
        api_models::payments::AmountFilter,
        api_models::mandates::MandateRevokedResponse,
        api_models::mandates::MandateResponse,
        api_models::mandates::MandateCardDetails,
        api_models::mandates::RecurringDetails,
        api_models::mandates::ProcessorPaymentToken,
        api_models::ephemeral_key::ClientSecretResponse,
        api_models::payments::CustomerDetails,
        api_models::payments::GiftCardData,
        api_models::payments::GiftCardDetails,
        api_models::payments::BHNGiftCardDetails,
        api_models::payments::MobilePaymentData,
        api_models::payments::MobilePaymentResponse,
        api_models::payments::Address,
        api_models::payouts::CardPayout,
        api_models::payouts::Wallet,
        api_models::payouts::Paypal,
        api_models::payouts::Venmo,
        api_models::payouts::AchBankTransfer,
        api_models::payouts::BacsBankTransfer,
        api_models::payouts::SepaBankTransfer,
        api_models::payouts::PixBankTransfer,
        api_models::payouts::PayoutRequest,
        api_models::payouts::PayoutAttemptResponse,
        api_models::payouts::PayoutActionRequest,
        api_models::payouts::PayoutCreateRequest,
        api_models::payouts::PayoutCreateResponse,
        api_models::payouts::PayoutListConstraints,
        api_models::payouts::PayoutListFilterConstraints,
        api_models::payouts::PayoutListResponse,
        api_models::payouts::PayoutRetrieveBody,
        api_models::payouts::PayoutRetrieveRequest,
        api_models::payouts::PayoutMethodData,
        api_models::payouts::PayoutMethodDataResponse,
        api_models::payouts::PayoutLinkResponse,
        api_models::payouts::Bank,
        api_models::payouts::PayoutCreatePayoutLinkConfig,
        api_models::enums::PayoutEntityType,
        api_models::enums::PayoutSendPriority,
        api_models::enums::PayoutStatus,
        api_models::enums::PayoutType,
        api_models::enums::TransactionType,
        api_models::enums::PresenceOfCustomerDuringPayment,
        api_models::enums::MitExemptionRequest,
        api_models::enums::EnablePaymentLinkRequest,
        api_models::enums::RequestIncrementalAuthorization,
        api_models::enums::SplitTxnsEnabled,
        api_models::enums::External3dsAuthenticationRequest,
        api_models::enums::TaxCalculationOverride,
        api_models::enums::SurchargeCalculationOverride,
        api_models::payments::FrmMessage,
        api_models::webhooks::OutgoingWebhook,
        api_models::webhooks::OutgoingWebhookContent,
        api_models::enums::EventClass,
        api_models::enums::EventType,
        api_models::enums::DecoupledAuthenticationType,
        api_models::enums::AuthenticationStatus,
        api_models::enums::UpdateActiveAttempt,
        api_models::admin::MerchantAccountResponse,
        api_models::admin::MerchantConnectorId,
        api_models::admin::MerchantDetails,
        api_models::admin::ToggleKVRequest,
        api_models::admin::ToggleKVResponse,
        api_models::admin::WebhookDetails,
        api_models::api_keys::ApiKeyExpiration,
        api_models::api_keys::CreateApiKeyRequest,
        api_models::api_keys::CreateApiKeyResponse,
        api_models::api_keys::RetrieveApiKeyResponse,
        api_models::api_keys::RevokeApiKeyResponse,
        api_models::api_keys::UpdateApiKeyRequest,
        api_models::payments::RetrievePaymentLinkRequest,
        api_models::payments::PaymentLinkResponse,
        api_models::payments::RetrievePaymentLinkResponse,
        api_models::payments::PaymentLinkInitiateRequest,
        api_models::payouts::PayoutLinkInitiateRequest,
        api_models::payments::ExtendedCardInfoResponse,
        api_models::payments::GooglePayAssuranceDetails,
        api_models::routing::RoutingConfigRequest,
        api_models::routing::RoutingDictionaryRecord,
        api_models::routing::RoutingKind,
        api_models::routing::RoutableConnectorChoice,
        api_models::routing::LinkedRoutingConfigRetrieveResponse,
        api_models::routing::RoutingRetrieveResponse,
        api_models::routing::ProfileDefaultRoutingConfig,
        api_models::routing::MerchantRoutingAlgorithm,
        api_models::routing::RoutingAlgorithmKind,
        api_models::routing::RoutingDictionary,
        api_models::routing::DynamicRoutingConfigParams,
        api_models::routing::SuccessBasedRoutingConfig,
        api_models::routing::SuccessRateSpecificityLevel,
        api_models::routing::CurrentBlockThreshold,
        api_models::open_router::DecisionEngineSuccessRateData,
        api_models::routing::ContractBasedTimeScale,
        api_models::routing::LabelInformation,
        api_models::routing::ContractBasedRoutingConfig,
        api_models::routing::ContractBasedRoutingConfigBody,
        api_models::open_router::DecisionEngineGatewayWiseExtraScore,
        api_models::open_router::DecisionEngineSRSubLevelInputConfig,
        api_models::open_router::DecisionEngineEliminationData,
        api_models::routing::SuccessBasedRoutingConfigBody,
        api_models::routing::RoutingAlgorithmWrapper,
        api_models::routing::EliminationRoutingConfig,
        api_models::open_router::DecisionEngineEliminationData,
        api_models::routing::EliminationAnalyserConfig,
        api_models::routing::DynamicRoutingAlgorithm,
        api_models::routing::StaticRoutingAlgorithm,
        api_models::routing::StraightThroughAlgorithm,
        api_models::routing::ConnectorVolumeSplit,
        api_models::routing::ConnectorSelection,
        api_models::routing::ast::RoutableChoiceKind,
        api_models::routing::ProgramThreeDsDecisionRule,
        api_models::routing::RuleThreeDsDecisionRule,
        api_models::enums::RoutableConnectors,
        api_models::routing::ast::ProgramConnectorSelection,
        api_models::routing::ast::RuleConnectorSelection,
        api_models::routing::ast::IfStatement,
        api_models::routing::ast::Comparison,
        api_models::routing::ast::ComparisonType,
        api_models::routing::ast::ValueType,
        api_models::routing::ast::MetadataValue,
        api_models::routing::ast::NumberComparison,
        api_models::routing::RoutingAlgorithmId,
        api_models::payment_methods::RequestPaymentMethodTypes,
        api_models::payments::PaymentLinkStatus,
        api_models::blocklist::BlocklistRequest,
        api_models::blocklist::BlocklistResponse,
        api_models::blocklist::ToggleBlocklistResponse,
        api_models::blocklist::ListBlocklistQuery,
        api_models::enums::BlocklistDataKind,
        api_models::enums::ErrorCategory,
        api_models::webhook_events::EventListItemResponse,
        api_models::webhook_events::EventRetrieveResponse,
        api_models::webhook_events::OutgoingWebhookRequestContent,
        api_models::webhook_events::OutgoingWebhookResponseContent,
        api_models::enums::WebhookDeliveryAttempt,
        api_models::enums::PaymentChargeType,
        api_models::enums::StripeChargeType,
        api_models::payments::CustomerDetailsResponse,
        api_models::payments::OpenBankingData,
        api_models::payments::OpenBankingSessionToken,
        api_models::payments::BankDebitResponse,
        api_models::payments::BankRedirectResponse,
        api_models::payments::BankTransferResponse,
        api_models::payments::CardRedirectResponse,
        api_models::payments::CardTokenResponse,
        api_models::payments::CryptoResponse,
        api_models::payments::GiftCardResponse,
        api_models::payments::OpenBankingResponse,
        api_models::payments::RealTimePaymentDataResponse,
        api_models::payments::UpiResponse,
        api_models::payments::VoucherResponse,
        api_models::payments::additional_info::CardTokenAdditionalData,
        api_models::payments::additional_info::BankDebitAdditionalData,
        api_models::payments::additional_info::AchBankDebitAdditionalData,
        api_models::payments::additional_info::BacsBankDebitAdditionalData,
        api_models::payments::additional_info::BecsBankDebitAdditionalData,
        api_models::payments::additional_info::SepaBankDebitAdditionalData,
        api_models::payments::additional_info::BankRedirectDetails,


pub struct PaypalLiabilityResponse {
    pub payment_source: CardParams,
}


pub fn is_permanent_card(&self) -> bool {
        matches!(self, Self::PermanentCard(_) | Self::Permanent(_))
    }


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

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

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = forte::ForteAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        let raw_basic_token = format!(
            "{}:{}",
            auth.api_access_id.peek(),
            auth.api_secret_key.peek()
        );
        let basic_token = format!("Basic {}", BASE64_ENGINE.encode(raw_basic_token));
        Ok(vec![
            (
                headers::AUTHORIZATION.to_string(),
                basic_token.into_masked(),
            ),
            (
                AUTH_ORG_ID_HEADER.to_string(),
                auth.organization_id.into_masked(),
            ),
        ])
    }
    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: forte::ForteErrorResponse = res
            .response
            .parse_struct("Forte ErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        let message = response.response.response_desc;
        let code = response
            .response
            .response_code
            .unwrap_or_else(|| NO_ERROR_CODE.to_string());
        Ok(ErrorResponse {
            status_code: res.status_code,
            code,
            message,
            reason: None,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


pub fn add_certificate_key(&mut self, certificate_key: Option<Secret<String>>) {
        self.certificate = certificate_key;
    }


File: crates/scheduler/src/producer.rs
 Public functions: 3

use std::sync::Arc;

use common_utils::{errors::CustomResult, id_type};
use diesel_models::enums::ProcessTrackerStatus;
use error_stack::{report, ResultExt};
use router_env::{
    instrument,
    tracing::{self, Instrument},
};
use time::Duration;
use tokio::sync::mpsc;

use super::{
    env::logger::{self, debug, error, warn},
    metrics,
};
use crate::{
    configs::settings::SchedulerSettings, errors, flow::SchedulerFlow,
    scheduler::SchedulerInterface, utils::*, SchedulerAppState, SchedulerSessionState,
};

#[instrument(skip_all)]
pub async fn start_producer<T, U, F>(
    state: &T,
    scheduler_settings: Arc<SchedulerSettings>,
    (tx, mut rx): (mpsc::Sender<()>, mpsc::Receiver<()>),
    app_state_to_session_state: F,
) -> CustomResult<(), errors::ProcessTrackerError>
where
    F: Fn(&T, &id_type::TenantId) -> CustomResult<U, errors::ProcessTrackerError>,
    T: SchedulerAppState,
    U: SchedulerSessionState,
{
    use std::time::Duration;

    use rand::distributions::{Distribution, Uniform};

    let mut rng = rand::thread_rng();

    // TODO: this can be removed once rand-0.9 is released
    // reference - https://github.com/rust-random/rand/issues/1326#issuecomment-1635331942
    #[allow(unknown_lints)]
    #[allow(clippy::unnecessary_fallible_conversions)]
    let timeout = Uniform::try_from(0..=scheduler_settings.loop_interval)
        .change_context(errors::ProcessTrackerError::ConfigurationError)?;

    tokio::time::sleep(Duration::from_millis(timeout.sample(&mut rng))).await;

    let mut interval =
        tokio::time::interval(Duration::from_millis(scheduler_settings.loop_interval));

    let mut shutdown_interval = tokio::time::interval(Duration::from_millis(
        scheduler_settings.graceful_shutdown_interval,
    ));

    let signal = common_utils::signals::get_allowed_signals()
        .map_err(|error| {
            logger::error!("Signal Handler Error: {:?}", error);
            errors::ProcessTrackerError::ConfigurationError
        })
        .attach_printable("Failed while creating a signals handler")?;
    let handle = signal.handle();
    let task_handle =
        tokio::spawn(common_utils::signals::signal_handler(signal, tx).in_current_span());

    loop {
        match rx.try_recv() {
            Err(mpsc::error::TryRecvError::Empty) => {
                interval.tick().await;
                let tenants = state.get_tenants();
                for tenant in tenants {
                    let session_state = app_state_to_session_state(state, &tenant)?;
                    match run_producer_flow(&session_state, &scheduler_settings).await {
                        Ok(_) => (),
                        Err(error) => {
                            // Intentionally not propagating error to caller.
                            // Any errors that occur in the producer flow must be handled here only, as
                            // this is the topmost level function which is concerned with the producer flow.
                            error!(?error);
                        }
                    }
                }
            }
            Ok(()) | Err(mpsc::error::TryRecvError::Disconnected) => {
                logger::debug!("Awaiting shutdown!");
                rx.close();
                shutdown_interval.tick().await;
                logger::info!("Terminating producer");
                break;
            }
        }
    }
    handle.close();
    task_handle
        .await
        .change_context(errors::ProcessTrackerError::UnexpectedFlow)?;

    Ok(())
}

#[instrument(skip_all)]
pub async fn run_producer_flow<T>(
    state: &T,
    settings: &SchedulerSettings,
) -> CustomResult<(), errors::ProcessTrackerError>
where
    T: SchedulerSessionState,
{
    lock_acquire_release::<_, _, _>(state.get_db().as_scheduler(), settings, move || async {
        let tasks = fetch_producer_tasks(state.get_db().as_scheduler(), settings).await?;
        debug!("Producer count of tasks {}", tasks.len());

        // [#268]: Allow task based segregation of tasks

        divide_and_append_tasks(
            state.get_db().as_scheduler(),
            SchedulerFlow::Producer,
            tasks,
            settings,
        )
        .await?;

        Ok(())
    })
    .await?;

    Ok(())
}

#[instrument(skip_all)]
pub async fn fetch_producer_tasks(
    db: &dyn SchedulerInterface,
    conf: &SchedulerSettings,
) -> CustomResult<Vec<storage::ProcessTracker>, errors::ProcessTrackerError> {
    let upper = conf.producer.upper_fetch_limit;
    let lower = conf.producer.lower_fetch_limit;
    let now = common_utils::date_time::now();
    // Add these to validations
    let time_upper_limit = now.checked_add(Duration::seconds(upper)).ok_or_else(|| {
        report!(errors::ProcessTrackerError::ConfigurationError)
            .attach_printable("Error obtaining upper limit to fetch producer tasks")
    })?;
    let time_lower_limit = now.checked_sub(Duration::seconds(lower)).ok_or_else(|| {
        report!(errors::ProcessTrackerError::ConfigurationError)
            .attach_printable("Error obtaining lower limit to fetch producer tasks")
    })?;

    let mut new_tasks = db
        .find_processes_by_time_status(
            time_lower_limit,
            time_upper_limit,
            ProcessTrackerStatus::New,
            None,
        )
        .await
        .change_context(errors::ProcessTrackerError::ProcessFetchingFailed)?;
    let mut pending_tasks = db
        .find_processes_by_time_status(
            time_lower_limit,
            time_upper_limit,
            ProcessTrackerStatus::Pending,
            None,
        )
        .await
        .change_context(errors::ProcessTrackerError::ProcessFetchingFailed)?;

    if new_tasks.is_empty() {
        warn!("No new tasks found for producer to schedule");
    }
    if pending_tasks.is_empty() {
        warn!("No pending tasks found for producer to schedule");
    }

    new_tasks.append(&mut pending_tasks);

    // Safety: Assuming we won't deal with more than `u64::MAX` tasks at once
    #[allow(clippy::as_conversions)]
    metrics::TASKS_PICKED_COUNT.add(new_tasks.len() as u64, &[]);
    Ok(new_tasks)
}


impl api::RefundSync for Nexixpay {}


pub struct SantanderPixPSyncResponse {
    #[serde(flatten)]
    pub base: SantanderPixQRCodePaymentsResponse,
    pub pix: Vec<SantanderPix>,
}


File: crates/router/src/core/payments/flows/incremental_authorization_flow.rs

use async_trait::async_trait;

use super::ConstructFlowSpecificData;
use crate::{
    core::{
        errors::{ConnectorErrorExt, RouterResult},
        payments::{self, access_token, helpers, transformers, Feature, PaymentData},
    },
    routes::SessionState,
    services,
    types::{self, api, domain},
};

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

    #[cfg(feature = "v2")]
    async fn construct_router_data<'a>(
        &self,
        state: &SessionState,
        connector_id: &str,
        merchant_context: &domain::MerchantContext,
        customer: &Option<domain::Customer>,
        merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
        merchant_recipient_data: Option<types::MerchantRecipientData>,
        header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
    ) -> RouterResult<types::PaymentsIncrementalAuthorizationRouterData> {
        todo!()
    }
}

#[async_trait]
impl Feature<api::IncrementalAuthorization, types::PaymentsIncrementalAuthorizationData>
    for types::RouterData<
        api::IncrementalAuthorization,
        types::PaymentsIncrementalAuthorizationData,
        types::PaymentsResponseData,
    >
{
    async fn decide_flows<'a>(
        self,
        state: &SessionState,
        connector: &api::ConnectorData,
        call_connector_action: payments::CallConnectorAction,
        connector_request: Option<services::Request>,
        _business_profile: &domain::Profile,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
        _return_raw_connector_response: Option<bool>,
    ) -> RouterResult<Self> {
        let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
            api::IncrementalAuthorization,
            types::PaymentsIncrementalAuthorizationData,
            types::PaymentsResponseData,
        > = connector.connector.get_connector_integration();

        let resp = services::execute_connector_processing_step(
            state,
            connector_integration,
            &self,
            call_connector_action,
            connector_request,
            None,
        )
        .await
        .to_payment_failed_response()?;

        Ok(resp)
    }

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

    async fn build_flow_specific_connector_request(
        &mut self,
        state: &SessionState,
        connector: &api::ConnectorData,
        call_connector_action: payments::CallConnectorAction,
    ) -> RouterResult<(Option<services::Request>, bool)> {
        let request = match call_connector_action {
            payments::CallConnectorAction::Trigger => {
                let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
                    api::IncrementalAuthorization,
                    types::PaymentsIncrementalAuthorizationData,
                    types::PaymentsResponseData,
                > = connector.connector.get_connector_integration();

                connector_integration
                    .build_request(self, &state.conf.connectors)
                    .to_payment_failed_response()?
            }
            _ => None,
        };

        Ok((request, true))
    }
}


impl api::PaymentCapture for Zsl {}


pub struct KatapultCard {
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
    complete: bool,
}