            .cloned();

        match maybe_mca {
            Some(mca) => mca
                .to_owned()
                .convert(
                    state,
                    key_store.key.get_inner(),
                    key_store.merchant_id.clone().into(),
                )
                .await
                .change_context(errors::StorageError::DecryptionError),
            None => Err(errors::StorageError::ValueNotFound(
                "cannot find merchant connector account".to_string(),
            )
            .into()),
        }
    }

    #[cfg(feature = "v1")]
    async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_connector_id: &common_utils::id_type::MerchantConnectorAccountId,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
        match self
            .merchant_connector_accounts
            .lock()
            .await
            .iter()
            .find(|account| {
                account.merchant_id == *merchant_id
                    && account.merchant_connector_id == *merchant_connector_id
            })
            .cloned()
            .async_map(|account| async {
                account
                    .convert(
                        state,
                        key_store.key.get_inner(),
                        key_store.merchant_id.clone().into(),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)
            })
            .await
        {
            Some(result) => result,
            None => {
                return Err(errors::StorageError::ValueNotFound(
                    "cannot find merchant connector account".to_string(),
                )
                .into())
            }
        }
    }

    #[cfg(feature = "v2")]
    async fn find_merchant_connector_account_by_id(
        &self,
        state: &KeyManagerState,
        id: &common_utils::id_type::MerchantConnectorAccountId,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
        match self
            .merchant_connector_accounts
            .lock()
            .await
            .iter()
            .find(|account| account.get_id() == *id)
            .cloned()
            .async_map(|account| async {
                account
                    .convert(
                        state,
                        key_store.key.get_inner(),
                        common_utils::types::keymanager::Identifier::Merchant(
                            key_store.merchant_id.clone(),
                        ),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)
            })
            .await
        {
            Some(result) => result,
            None => {
                return Err(errors::StorageError::ValueNotFound(
                    "cannot find merchant connector account".to_string(),
                )
                .into())
            }
        }
    }

    #[cfg(feature = "v1")]
    async fn insert_merchant_connector_account(
        &self,
        state: &KeyManagerState,
        t: domain::MerchantConnectorAccount,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
        let mut accounts = self.merchant_connector_accounts.lock().await;
        let account = storage::MerchantConnectorAccount {
            merchant_id: t.merchant_id,
            connector_name: t.connector_name,
            connector_account_details: t.connector_account_details.into(),
            test_mode: t.test_mode,
            disabled: t.disabled,
            merchant_connector_id: t.merchant_connector_id.clone(),
            id: Some(t.merchant_connector_id),
            payment_methods_enabled: t.payment_methods_enabled,
            metadata: t.metadata,
            frm_configs: None,
            frm_config: t.frm_configs,
            connector_type: t.connector_type,
            connector_label: t.connector_label,
            business_country: t.business_country,
            business_label: t.business_label,
            business_sub_label: t.business_sub_label,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            connector_webhook_details: t.connector_webhook_details,
            profile_id: Some(t.profile_id),
            applepay_verified_domains: t.applepay_verified_domains,
            pm_auth_config: t.pm_auth_config,
            status: t.status,
            connector_wallets_details: t.connector_wallets_details.map(Encryption::from),
            additional_merchant_data: t.additional_merchant_data.map(|data| data.into()),
            version: t.version,
        };
        accounts.push(account.clone());
        account
            .convert(
                state,
                key_store.key.get_inner(),
                key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[cfg(feature = "v2")]
    async fn insert_merchant_connector_account(
        &self,
        state: &KeyManagerState,
        t: domain::MerchantConnectorAccount,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
        let mut accounts = self.merchant_connector_accounts.lock().await;
        let account = storage::MerchantConnectorAccount {
            id: t.id,
            merchant_id: t.merchant_id,
            connector_name: t.connector_name,
            connector_account_details: t.connector_account_details.into(),
            disabled: t.disabled,
            payment_methods_enabled: t.payment_methods_enabled,
            metadata: t.metadata,
            frm_config: t.frm_configs,
            connector_type: t.connector_type,
            connector_label: t.connector_label,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            connector_webhook_details: t.connector_webhook_details,
            profile_id: t.profile_id,
            applepay_verified_domains: t.applepay_verified_domains,
            pm_auth_config: t.pm_auth_config,
            status: t.status,
            connector_wallets_details: t.connector_wallets_details.map(Encryption::from),
            additional_merchant_data: t.additional_merchant_data.map(|data| data.into()),
            version: t.version,
            feature_metadata: t.feature_metadata.map(From::from),
        };
        accounts.push(account.clone());
        account
            .convert(
                state,
                key_store.key.get_inner(),
                common_utils::types::keymanager::Identifier::Merchant(
                    key_store.merchant_id.clone(),
                ),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn find_merchant_connector_account_by_merchant_id_and_disabled_list(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        get_disabled: bool,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantConnectorAccounts, errors::StorageError> {
        let accounts = self
            .merchant_connector_accounts
            .lock()
            .await
            .iter()
            .filter(|account: &&storage::MerchantConnectorAccount| {
                if get_disabled {
                    account.merchant_id == *merchant_id
                } else {
                    account.merchant_id == *merchant_id && account.disabled == Some(false)
                }
            })
            .cloned()
            .collect::<Vec<storage::MerchantConnectorAccount>>();

        let mut output = Vec::with_capacity(accounts.len());
        for account in accounts.into_iter() {
            output.push(
                account
                    .convert(
                        state,
                        key_store.key.get_inner(),
                        key_store.merchant_id.clone().into(),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)?,
            )
        }
        Ok(domain::MerchantConnectorAccounts::new(output))
    }

    #[cfg(all(feature = "olap", feature = "v2"))]
    async fn list_connector_account_by_profile_id(
        &self,
        state: &KeyManagerState,
        profile_id: &common_utils::id_type::ProfileId,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<Vec<domain::MerchantConnectorAccount>, errors::StorageError> {
        let accounts = self
            .merchant_connector_accounts
            .lock()
            .await
            .iter()
            .filter(|account: &&storage::MerchantConnectorAccount| {
                account.profile_id == *profile_id
            })
            .cloned()
            .collect::<Vec<storage::MerchantConnectorAccount>>();

        let mut output = Vec::with_capacity(accounts.len());
        for account in accounts.into_iter() {
            output.push(
                account
                    .convert(
                        state,
                        key_store.key.get_inner(),
                        key_store.merchant_id.clone().into(),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)?,
            )
        }
        Ok(output)
    }

    #[cfg(feature = "v1")]
    async fn update_merchant_connector_account(
        &self,
        state: &KeyManagerState,
        this: domain::MerchantConnectorAccount,
        merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
        let mca_update_res = self
            .merchant_connector_accounts
            .lock()
            .await
            .iter_mut()
            .find(|account| account.merchant_connector_id == this.merchant_connector_id)
            .map(|a| {
                let updated =
                    merchant_connector_account.create_merchant_connector_account(a.clone());
                *a = updated.clone();
                updated
            })
            .async_map(|account| async {
                account
                    .convert(
                        state,
                        key_store.key.get_inner(),
                        key_store.merchant_id.clone().into(),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)
            })
            .await;

        match mca_update_res {
            Some(result) => result,
            None => {
                return Err(errors::StorageError::ValueNotFound(
                    "cannot find merchant connector account to update".to_string(),
                )
                .into())
            }
        }
    }

    #[cfg(feature = "v2")]
    async fn update_merchant_connector_account(
        &self,
        state: &KeyManagerState,
        this: domain::MerchantConnectorAccount,
        merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal,
        key_store: &domain::MerchantKeyStore,
    ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
        let mca_update_res = self
            .merchant_connector_accounts
            .lock()
            .await
            .iter_mut()
            .find(|account| account.get_id() == this.get_id())
            .map(|a| {
                let updated =
                    merchant_connector_account.create_merchant_connector_account(a.clone());
                *a = updated.clone();
                updated
            })
            .async_map(|account| async {
                account
                    .convert(
                        state,
                        key_store.key.get_inner(),
                        common_utils::types::keymanager::Identifier::Merchant(
                            key_store.merchant_id.clone(),
                        ),
                    )
                    .await
                    .change_context(errors::StorageError::DecryptionError)
            })
            .await;

        match mca_update_res {
            Some(result) => result,
            None => {
                return Err(errors::StorageError::ValueNotFound(
                    "cannot find merchant connector account to update".to_string(),
                )
                .into())
            }
        }
    }

    #[cfg(feature = "v1")]
    async fn delete_merchant_connector_account_by_merchant_id_merchant_connector_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_connector_id: &common_utils::id_type::MerchantConnectorAccountId,
    ) -> CustomResult<bool, errors::StorageError> {
        let mut accounts = self.merchant_connector_accounts.lock().await;
        match accounts.iter().position(|account| {
            account.merchant_id == *merchant_id
                && account.merchant_connector_id == *merchant_connector_id
        }) {
            Some(index) => {
                accounts.remove(index);
                return Ok(true);
            }
            None => {
                return Err(errors::StorageError::ValueNotFound(
                    "cannot find merchant connector account to delete".to_string(),
                )
                .into())
            }
        }
    }

    #[cfg(feature = "v2")]
    async fn delete_merchant_connector_account_by_id(
        &self,
        id: &common_utils::id_type::MerchantConnectorAccountId,
    ) -> CustomResult<bool, errors::StorageError> {
        let mut accounts = self.merchant_connector_accounts.lock().await;
        match accounts.iter().position(|account| account.get_id() == *id) {
            Some(index) => {
                accounts.remove(index);
                return Ok(true);
            }
            None => {
                return Err(errors::StorageError::ValueNotFound(
                    "cannot find merchant connector account to delete".to_string(),
                )
                .into())
            }
        }
    }
}

#[cfg(feature = "accounts_cache")]
#[cfg(test)]
mod merchant_connector_account_cache_tests {
    use std::sync::Arc;

    #[cfg(feature = "v1")]
    use api_models::enums::CountryAlpha2;
    use common_utils::{date_time, type_name, types::keymanager::Identifier};
    use diesel_models::enums::ConnectorType;
    use error_stack::ResultExt;
    use masking::PeekInterface;
    use storage_impl::redis::{
        cache::{self, CacheKey, CacheKind, ACCOUNTS_CACHE},
        kv_store::RedisConnInterface,
        pub_sub::PubSubInterface,
    };
    use time::macros::datetime;
    use tokio::sync::oneshot;

    use crate::{
        core::errors,
        db::{
            merchant_connector_account::MerchantConnectorAccountInterface,
            merchant_key_store::MerchantKeyStoreInterface, MasterKeyInterface, MockDb,
        },
        routes::{
            self,
            app::{settings::Settings, StorageImpl},
        },
        services,
        types::{
            domain::{self, behaviour::Conversion},
            storage,
        },
    };

    #[allow(clippy::unwrap_used)]
    #[tokio::test]
    #[cfg(feature = "v1")]
    async fn test_connector_profile_id_cache() {
        let conf = Settings::new().unwrap();
        let tx: oneshot::Sender<()> = oneshot::channel().0;

        let app_state = Box::pin(routes::AppState::with_storage(
            conf,
            StorageImpl::PostgresqlTest,
            tx,
            Box::new(services::MockApiClient),
        ))
        .await;

        let state = &Arc::new(app_state)
            .get_session_state(
                &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(),
                None,
                || {},
            )
            .unwrap();
        #[allow(clippy::expect_used)]
        let db = MockDb::new(&redis_interface::RedisSettings::default())
            .await
            .expect("Failed to create Mock store");

        let redis_conn = db.get_redis_conn().unwrap();
        let master_key = db.get_master_key();
        redis_conn
            .subscribe("hyperswitch_invalidate")
            .await
            .unwrap();

        let merchant_id =
            common_utils::id_type::MerchantId::try_from(std::borrow::Cow::from("test_merchant"))
                .unwrap();

        let connector_label = "stripe_USA";
        let merchant_connector_id = "simple_merchant_connector_id";
        let profile_id =
            common_utils::id_type::ProfileId::try_from(std::borrow::Cow::from("pro_max_ultra"))
                .unwrap();
        let key_manager_state = &state.into();
        db.insert_merchant_key_store(
            key_manager_state,
            domain::MerchantKeyStore {
                merchant_id: merchant_id.clone(),
                key: domain::types::crypto_operation(
                    key_manager_state,
                    type_name!(domain::MerchantKeyStore),
                    domain::types::CryptoOperation::Encrypt(
                        services::generate_aes256_key().unwrap().to_vec().into(),
                    ),
                    Identifier::Merchant(merchant_id.clone()),
                    master_key,
                )
                .await
                .and_then(|val| val.try_into_operation())
                .unwrap(),
                created_at: datetime!(2023-02-01 0:00),
            },
            &master_key.to_vec().into(),
        )
        .await
        .unwrap();

        let merchant_key = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_id,
                &master_key.to_vec().into(),
            )
            .await
            .unwrap();

        let mca = domain::MerchantConnectorAccount {
            merchant_id: merchant_id.to_owned(),
            connector_name: "stripe".to_string(),
            connector_account_details: domain::types::crypto_operation(
                key_manager_state,
                type_name!(domain::MerchantConnectorAccount),
                domain::types::CryptoOperation::Encrypt(serde_json::Value::default().into()),
                Identifier::Merchant(merchant_key.merchant_id.clone()),
                merchant_key.key.get_inner().peek(),
            )
            .await
            .and_then(|val| val.try_into_operation())
            .unwrap(),
            test_mode: None,
            disabled: None,
            merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId::wrap(
                merchant_connector_id.to_string(),
            )
            .unwrap(),
            payment_methods_enabled: None,
            connector_type: ConnectorType::FinOperations,
            metadata: None,
            frm_configs: None,
            connector_label: Some(connector_label.to_string()),
            business_country: Some(CountryAlpha2::US),
            business_label: Some("cloth".to_string()),
            business_sub_label: None,
            created_at: date_time::now(),
            modified_at: date_time::now(),
            connector_webhook_details: None,
            profile_id: profile_id.to_owned(),
            applepay_verified_domains: None,
            pm_auth_config: None,
            status: common_enums::ConnectorStatus::Inactive,
            connector_wallets_details: Some(
                domain::types::crypto_operation(
                    key_manager_state,
                    type_name!(domain::MerchantConnectorAccount),
                    domain::types::CryptoOperation::Encrypt(serde_json::Value::default().into()),
                    Identifier::Merchant(merchant_key.merchant_id.clone()),
                    merchant_key.key.get_inner().peek(),
                )
                .await
                .and_then(|val| val.try_into_operation())
                .unwrap(),
            ),
            additional_merchant_data: None,
            version: common_types::consts::API_VERSION,
        };

        db.insert_merchant_connector_account(key_manager_state, mca.clone(), &merchant_key)
            .await
            .unwrap();

        let find_call = || async {
            Conversion::convert(
                db.find_merchant_connector_account_by_profile_id_connector_name(
                    key_manager_state,
                    &profile_id,
                    &mca.connector_name,
                    &merchant_key,
                )
                .await
                .unwrap(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
        };
        let _: storage::MerchantConnectorAccount = cache::get_or_populate_in_memory(
            &db,
            &format!(
                "{}_{}",
                merchant_id.get_string_repr(),
                profile_id.get_string_repr(),
            ),
            find_call,
            &ACCOUNTS_CACHE,
        )
        .await
        .unwrap();

        let delete_call = || async {
            db.delete_merchant_connector_account_by_merchant_id_merchant_connector_id(
                &merchant_id,
                &common_utils::id_type::MerchantConnectorAccountId::wrap(
                    merchant_connector_id.to_string(),
                )
                .unwrap(),
            )
            .await
        };

        cache::publish_and_redact(
            &db,
            CacheKind::Accounts(
                format!("{}_{}", merchant_id.get_string_repr(), connector_label).into(),
            ),
            delete_call,
        )
        .await
        .unwrap();

        assert!(ACCOUNTS_CACHE
            .get_val::<domain::MerchantConnectorAccount>(CacheKey {
                key: format!("{}_{}", merchant_id.get_string_repr(), connector_label),
                prefix: String::default(),
            },)
            .await
            .is_none())
    }

    #[allow(clippy::unwrap_used)]
    #[tokio::test]
    #[cfg(feature = "v2")]
    async fn test_connector_profile_id_cache() {
        let conf = Settings::new().unwrap();
        let tx: oneshot::Sender<()> = oneshot::channel().0;

        let app_state = Box::pin(routes::AppState::with_storage(
            conf,
            StorageImpl::PostgresqlTest,
            tx,
            Box::new(services::MockApiClient),
        ))
        .await;
        let state = &Arc::new(app_state)
            .get_session_state(
                &common_utils::id_type::TenantId::try_from_string("public".to_string()).unwrap(),
                None,
                || {},
            )
            .unwrap();
        #[allow(clippy::expect_used)]
        let db = MockDb::new(&redis_interface::RedisSettings::default())
            .await
            .expect("Failed to create Mock store");

        let redis_conn = db.get_redis_conn().unwrap();
        let master_key = db.get_master_key();
        redis_conn
            .subscribe("hyperswitch_invalidate")
            .await
            .unwrap();

        let merchant_id =
            common_utils::id_type::MerchantId::try_from(std::borrow::Cow::from("test_merchant"))
                .unwrap();
        let connector_label = "stripe_USA";
        let id = common_utils::generate_merchant_connector_account_id_of_default_length();
        let profile_id =
            common_utils::id_type::ProfileId::try_from(std::borrow::Cow::from("pro_max_ultra"))
                .unwrap();
        let key_manager_state = &state.into();
        db.insert_merchant_key_store(
            key_manager_state,
            domain::MerchantKeyStore {
                merchant_id: merchant_id.clone(),
                key: domain::types::crypto_operation(
                    key_manager_state,
                    type_name!(domain::MerchantConnectorAccount),
                    domain::types::CryptoOperation::Encrypt(
                        services::generate_aes256_key().unwrap().to_vec().into(),
                    ),
                    Identifier::Merchant(merchant_id.clone()),
                    master_key,
                )
                .await
                .and_then(|val| val.try_into_operation())
                .unwrap(),
                created_at: datetime!(2023-02-01 0:00),
            },
            &master_key.to_vec().into(),
        )
        .await
        .unwrap();

        let merchant_key = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_id,
                &master_key.to_vec().into(),
            )
            .await
            .unwrap();

        let mca = domain::MerchantConnectorAccount {
            id: id.clone(),
            merchant_id: merchant_id.clone(),
            connector_name: common_enums::connector_enums::Connector::Stripe,
            connector_account_details: domain::types::crypto_operation(
                key_manager_state,
                type_name!(domain::MerchantConnectorAccount),
                domain::types::CryptoOperation::Encrypt(serde_json::Value::default().into()),
                Identifier::Merchant(merchant_key.merchant_id.clone()),
                merchant_key.key.get_inner().peek(),
            )
            .await
            .and_then(|val| val.try_into_operation())
            .unwrap(),
            disabled: None,
            payment_methods_enabled: None,
            connector_type: ConnectorType::FinOperations,
            metadata: None,
            frm_configs: None,
            connector_label: Some(connector_label.to_string()),
            created_at: date_time::now(),
            modified_at: date_time::now(),
            connector_webhook_details: None,
            profile_id: profile_id.to_owned(),
            applepay_verified_domains: None,
            pm_auth_config: None,
            status: common_enums::ConnectorStatus::Inactive,
            connector_wallets_details: Some(
                domain::types::crypto_operation(
                    key_manager_state,
                    type_name!(domain::MerchantConnectorAccount),
                    domain::types::CryptoOperation::Encrypt(serde_json::Value::default().into()),
                    Identifier::Merchant(merchant_key.merchant_id.clone()),
                    merchant_key.key.get_inner().peek(),
                )
                .await
                .and_then(|val| val.try_into_operation())
                .unwrap(),
            ),
            additional_merchant_data: None,
            version: common_types::consts::API_VERSION,
            feature_metadata: None,
        };

        db.insert_merchant_connector_account(key_manager_state, mca.clone(), &merchant_key)
            .await
            .unwrap();

        let find_call = || async {
            #[cfg(feature = "v1")]
            let mca = db
                .find_merchant_connector_account_by_profile_id_connector_name(
                    key_manager_state,
                    profile_id,
                    &mca.connector_name,
                    &merchant_key,
                )
                .await
                .unwrap();
            #[cfg(feature = "v2")]
            let mca: domain::MerchantConnectorAccount = { todo!() };
            Conversion::convert(mca)
                .await
                .change_context(errors::StorageError::DecryptionError)
        };

        let _: storage::MerchantConnectorAccount = cache::get_or_populate_in_memory(
            &db,
            &format!(
                "{}_{}",
                merchant_id.clone().get_string_repr(),
                profile_id.get_string_repr()
            ),
            find_call,
            &ACCOUNTS_CACHE,
        )
        .await
        .unwrap();

        let delete_call = || async { db.delete_merchant_connector_account_by_id(&id).await };

        cache::publish_and_redact(
            &db,
            CacheKind::Accounts(
                format!("{}_{}", merchant_id.get_string_repr(), connector_label).into(),
            ),
            delete_call,
        )
        .await
        .unwrap();

        assert!(ACCOUNTS_CACHE
            .get_val::<domain::MerchantConnectorAccount>(CacheKey {
                key: format!("{}_{}", merchant_id.get_string_repr(), connector_label),
                prefix: String::default(),
            },)
            .await
            .is_none())
    }
}


Documentation: api-reference/v2/tokenization/tokenization--create.mdx
# Type: Doc File

---
openapi: post /v2/tokenize
---


pub struct PixBankTransferAdditionalData {
    /// Partially masked unique key for pix transfer
    #[schema(value_type = String, example = "a1f4102e ****** 6fa48899c1d1")]
    pub pix_key: MaskedBankAccount,

    /// Partially masked CPF - CPF is a Brazilian tax identification number
    #[schema(value_type = Option<String>, example = "**** 124689")]
    pub tax_id: Option<MaskedBankAccount>,

    /// Bank account number is an unique identifier assigned by a bank to a customer.
    #[schema(value_type = String, example = "**** 23456")]
    pub bank_account_number: MaskedBankAccount,

    /// Bank name
    #[schema(value_type = Option<String>, example = "Deutsche Bank")]
    pub bank_name: Option<String>,

    /// Bank branch
    #[schema(value_type = Option<String>, example = "3707")]
    pub bank_branch: Option<String>,
}


pub struct VerificationResults {
    pub address_verification_code: String,
    pub cvv2_verification_code: String,
}


OpenAPI Block Path: components.schemas.RefundRequest
{
  "type": "object",
  "required": [
    "payment_id"
  ],
  "properties": {
    "payment_id": {
      "type": "string",
      "description": "The payment id against which refund is to be initiated",
      "example": "pay_mbabizu24mvu3mela5njyhpit4",
      "maxLength": 30,
      "minLength": 30
    },
    "refund_id": {
      "type": "string",
      "description": "Unique Identifier for the Refund. This is to ensure idempotency for multiple partial refunds initiated against the same payment. If this is not passed by the merchant, this field shall be auto generated and provided in the API response. It is recommended to generate uuid(v4) as the refund_id.",
      "example": "ref_mbabizu24mvu3mela5njyhpit4",
      "nullable": true,
      "maxLength": 30,
      "minLength": 30
    },
    "merchant_id": {
      "type": "string",
      "description": "The identifier for the Merchant Account",
      "example": "y3oqhf46pyzuxjbcn2giaqnb44",
      "nullable": true,
      "maxLength": 255
    },
    "amount": {
      "type": "integer",
      "format": "int64",
      "description": "Total amount for which the refund is to be initiated. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., If not provided, this will default to the full payment amount",
      "example": 6540,
      "nullable": true,
      "minimum": 100
    },
    "reason": {
      "type": "string",
      "description": "Reason for the refund. Often useful for displaying to users and your customer support executive. In case the payment went through Stripe, this field needs to be passed with one of these enums: `duplicate`, `fraudulent`, or `requested_by_customer`",
      "example": "Customer returned the product",
      "nullable": true,
      "maxLength": 255
    },
    "refund_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/RefundType"
        }
      ],
      "default": "Instant",
      "nullable": true
    },
    "metadata": {
      "type": "object",
      "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.",
      "nullable": true
    },
    "merchant_connector_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MerchantConnectorDetailsWrap"
        }
      ],
      "nullable": true
    },
    "split_refunds": {
      "allOf": [
        {
          "$ref": "#/components/schemas/SplitRefund"
        }
      ],
      "nullable": true
    }
  },
  "additionalProperties": false
}


                .change_context(errors::ConnectorError::NoConnectorMetaData)
                .attach_printable(format!(
                    "cannot parse gpay metadata from the given value {connector_metadata:?}"
                ))
                .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                    field_name: "connector_metadata".to_string(),
                    expected_format: "gpay_metadata_format".to_string(),
                })?;

            let gpay_allowed_payment_methods = gpay_data
                .data
                .allowed_payment_methods
                .into_iter()
                .map(
                    |allowed_payment_methods| payment_types::GpayAllowedPaymentMethods {
                        parameters: payment_types::GpayAllowedMethodsParameters {
                            billing_address_required: Some(is_billing_details_required),
                            billing_address_parameters: billing_address_parameters.clone(),
                            ..allowed_payment_methods.parameters
                        },
                        ..allowed_payment_methods
                    },
                )
                .collect();

            Ok(types::PaymentsSessionRouterData {
                response: Ok(types::PaymentsResponseData::SessionResponse {
                    session_token: payment_types::SessionToken::GooglePay(Box::new(
                        payment_types::GpaySessionTokenResponse::GooglePaySession(
                            payment_types::GooglePaySessionResponse {
                                merchant_info: gpay_data.data.merchant_info,
                                allowed_payment_methods: gpay_allowed_payment_methods,
                                transaction_info,
                                connector: connector.connector_name.to_string(),
                                sdk_next_action: payment_types::SdkNextAction {
                                    next_action: payment_types::NextActionCall::Confirm,
                                },
                                delayed_session_token: false,
                                secrets: None,
                                shipping_address_required: required_shipping_contact_fields,
                                // We pass Email as a required field irrespective of
                                // collect_billing_details_from_wallet_connector or
                                // collect_shipping_details_from_wallet_connector as it is common to both.
                                email_required: required_shipping_contact_fields
                                    || is_billing_details_required,
                                shipping_address_parameters:
                                    api_models::payments::GpayShippingAddressParameters {
                                        phone_number_required: required_shipping_contact_fields,
                                    },
                            },
                        ),
                    )),
                }),
                ..router_data.clone()
            })
        }
    }
}

/// Card Type for Google Pay Allowerd Payment Methods
pub(crate) const CARD: &str = "CARD";

fn get_allowed_payment_methods_from_cards(
    gpay_info: payment_types::GooglePayWalletDetails,
    gpay_token_specific_data: &payment_types::GooglePayTokenizationSpecification,
    is_billing_details_required: bool,
) -> RouterResult<payment_types::GpayAllowedPaymentMethods> {
    let billing_address_parameters =
        is_billing_details_required.then_some(payment_types::GpayBillingAddressParameters {
            phone_number_required: is_billing_details_required,
            format: payment_types::GpayBillingAddressFormat::FULL,
        });

    let protocol_version: Option<String> = gpay_token_specific_data
        .parameters
        .public_key
        .as_ref()
        .map(|_| PROTOCOL.to_string());

    Ok(payment_types::GpayAllowedPaymentMethods {
        parameters: payment_types::GpayAllowedMethodsParameters {
            billing_address_required: Some(is_billing_details_required),
            billing_address_parameters: billing_address_parameters.clone(),
            ..gpay_info.google_pay.cards
        },
        payment_method_type: CARD.to_string(),
        tokenization_specification: payment_types::GpayTokenizationSpecification {
            token_specification_type: gpay_token_specific_data.tokenization_type.to_string(),
            parameters: payment_types::GpayTokenParameters {
                protocol_version,
                public_key: gpay_token_specific_data.parameters.public_key.clone(),
                gateway: gpay_token_specific_data.parameters.gateway.clone(),
                gateway_merchant_id: gpay_token_specific_data
                    .parameters
                    .gateway_merchant_id
                    .clone()
                    .expose_option(),
                stripe_publishable_key: gpay_token_specific_data
                    .parameters
                    .stripe_publishable_key
                    .clone()
                    .expose_option(),
                stripe_version: gpay_token_specific_data
                    .parameters
                    .stripe_version
                    .clone()
                    .expose_option(),
            },
        },
    })
}

fn is_session_response_delayed(
    state: &routes::SessionState,
    connector: &api::ConnectorData,
) -> bool {
    let connectors_with_delayed_response = &state
        .conf
        .delayed_session_response
        .connectors_with_delayed_session_response;

    connectors_with_delayed_response.contains(&connector.connector_name)
}

fn log_session_response_if_error(
    response: &Result<Result<types::Response, types::Response>, Report<errors::ApiClientError>>,
) {
    if let Err(error) = response.as_ref() {
        logger::error!(?error);
    };
    response
        .as_ref()
        .ok()
        .map(|res| res.as_ref().map_err(|error| logger::error!(?error)));
}

#[async_trait]
pub trait RouterDataSession
where
    Self: Sized,
{
    async fn decide_flow<'a, 'b>(
        &'b self,
        state: &'a routes::SessionState,
        connector: &api::ConnectorData,
        _confirm: Option<bool>,
        call_connector_action: payments::CallConnectorAction,
        business_profile: &domain::Profile,
        header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<Self>;
}

fn create_paypal_sdk_session_token(
    _state: &routes::SessionState,
    router_data: &types::PaymentsSessionRouterData,
    connector: &api::ConnectorData,
    _business_profile: &domain::Profile,
) -> RouterResult<types::PaymentsSessionRouterData> {
    let connector_metadata = router_data.connector_meta_data.clone();

    let paypal_sdk_data = connector_metadata
        .clone()
        .parse_value::<payment_types::PaypalSdkSessionTokenData>("PaypalSdkSessionTokenData")
        .change_context(errors::ConnectorError::NoConnectorMetaData)
        .attach_printable(format!(
            "cannot parse paypal_sdk metadata from the given value {connector_metadata:?}"
        ))
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "connector_metadata".to_string(),
            expected_format: "paypal_sdk_metadata_format".to_string(),
        })?;

    Ok(types::PaymentsSessionRouterData {
        response: Ok(types::PaymentsResponseData::SessionResponse {
            session_token: payment_types::SessionToken::Paypal(Box::new(
                payment_types::PaypalSessionTokenResponse {
                    connector: connector.connector_name.to_string(),
                    session_token: paypal_sdk_data.data.client_id,
                    sdk_next_action: payment_types::SdkNextAction {
                        next_action: payment_types::NextActionCall::PostSessionTokens,
                    },
                },
            )),
        }),
        ..router_data.clone()
    })
}

async fn create_amazon_pay_session_token(
    router_data: &types::PaymentsSessionRouterData,
    state: &routes::SessionState,
) -> RouterResult<types::PaymentsSessionRouterData> {
    let amazon_pay_session_token_data = router_data
        .connector_wallets_details
        .clone()
        .parse_value::<payment_types::AmazonPaySessionTokenData>("AmazonPaySessionTokenData")
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "merchant_id or store_id",
        })?;
    let amazon_pay_metadata = amazon_pay_session_token_data.data;
    let merchant_id = amazon_pay_metadata.merchant_id;
    let store_id = amazon_pay_metadata.store_id;
    let amazonpay_supported_currencies =
        payments::cards::list_countries_currencies_for_connector_payment_method_util(
            state.conf.pm_filters.clone(),
            enums::Connector::Amazonpay,
            enums::PaymentMethodType::AmazonPay,
        )
        .await
        .currencies;
    // currently supports only the US region hence USD is the only supported currency
    payment_types::AmazonPayDeliveryOptions::validate_currency(
        router_data.request.currency,
        amazonpay_supported_currencies.clone(),
    )
    .change_context(errors::ApiErrorResponse::CurrencyNotSupported {
        message: "USD is the only supported currency.".to_string(),
    })?;
    let ledger_currency = router_data.request.currency;
    // currently supports only the 'automatic' capture_method
    let payment_intent = payment_types::AmazonPayPaymentIntent::AuthorizeWithCapture;
    let required_amount_type = StringMajorUnitForConnector;
    let total_tax_amount = required_amount_type
        .convert(
            router_data.request.order_tax_amount.unwrap_or_default(),
            router_data.request.currency,
        )
        .change_context(errors::ApiErrorResponse::AmountConversionFailed {
            amount_type: "StringMajorUnit",
        })?;
    let total_base_amount = required_amount_type
        .convert(
            router_data.request.minor_amount,
            router_data.request.currency,
        )
        .change_context(errors::ApiErrorResponse::AmountConversionFailed {
            amount_type: "StringMajorUnit",
        })?;

    let delivery_options_request = router_data
        .request
        .metadata
        .clone()
        .and_then(|metadata| {
            metadata
                .expose()
                .get("delivery_options")
                .and_then(|value| value.as_array().cloned())
        })
        .ok_or(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "metadata.delivery_options",
        })?;

    let mut delivery_options =
        payment_types::AmazonPayDeliveryOptions::parse_delivery_options_request(
            &delivery_options_request,
        )
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "delivery_options".to_string(),
            expected_format: r#""delivery_options": [{"id": String, "price": {"amount": Number, "currency_code": String}, "shipping_method":{"shipping_method_name": String, "shipping_method_code": String}, "is_default": Boolean}]"#.to_string(),
        })?;

    let default_amount = payment_types::AmazonPayDeliveryOptions::get_default_delivery_amount(
        delivery_options.clone(),
    )
    .change_context(errors::ApiErrorResponse::InvalidDataValue {
        field_name: "is_default",
    })?;

    for option in &delivery_options {
        payment_types::AmazonPayDeliveryOptions::validate_currency(
            option.price.currency_code,
            amazonpay_supported_currencies.clone(),
        )
        .change_context(errors::ApiErrorResponse::CurrencyNotSupported {
            message: "USD is the only supported currency.".to_string(),
        })?;
    }

    payment_types::AmazonPayDeliveryOptions::insert_display_amount(
        &mut delivery_options,
        router_data.request.currency,
    )
    .change_context(errors::ApiErrorResponse::AmountConversionFailed {
        amount_type: "StringMajorUnit",
    })?;

    let total_shipping_amount = match router_data.request.shipping_cost {
        Some(shipping_cost) => {
            if shipping_cost == default_amount {
                required_amount_type
                    .convert(shipping_cost, router_data.request.currency)
                    .change_context(errors::ApiErrorResponse::AmountConversionFailed {
                        amount_type: "StringMajorUnit",
                    })?
            } else {
                return Err(errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "shipping_cost",
                })
                .attach_printable(format!(
                    "Provided shipping_cost ({shipping_cost}) does not match the default delivery amount ({default_amount})"
                ));
            }
        }
        None => {
            return Err(errors::ApiErrorResponse::MissingRequiredField {
                field_name: "shipping_cost",
            }
            .into());
        }
    };

    Ok(types::PaymentsSessionRouterData {
        response: Ok(types::PaymentsResponseData::SessionResponse {
            session_token: payment_types::SessionToken::AmazonPay(Box::new(
                payment_types::AmazonPaySessionTokenResponse {
                    merchant_id,
                    ledger_currency,
                    store_id,
                    payment_intent,
                    total_shipping_amount,
                    total_tax_amount,
                    total_base_amount,
                    delivery_options,
                },
            )),
        }),
        ..router_data.clone()
    })
}

#[async_trait]
impl RouterDataSession for types::PaymentsSessionRouterData {
    async fn decide_flow<'a, 'b>(
        &'b self,
        state: &'a routes::SessionState,
        connector: &api::ConnectorData,
        _confirm: Option<bool>,
        call_connector_action: payments::CallConnectorAction,
        business_profile: &domain::Profile,
        header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<Self> {
        match connector.get_token {
            api::GetToken::GpayMetadata => {
                create_gpay_session_token(state, self, connector, business_profile)
            }
            api::GetToken::SamsungPayMetadata => create_samsung_pay_session_token(
                state,
                self,
                header_payload,
                connector,
                business_profile,
            ),
            api::GetToken::ApplePayMetadata => {
                create_applepay_session_token(
                    state,
                    self,
                    connector,
                    business_profile,
                    header_payload,
                )
                .await
            }
            api::GetToken::PaypalSdkMetadata => {
                create_paypal_sdk_session_token(state, self, connector, business_profile)
            }
            api::GetToken::PazeMetadata => create_paze_session_token(self, header_payload),
            api::GetToken::Connector => {
                let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
                    api::Session,
                    types::PaymentsSessionData,
                    types::PaymentsResponseData,
                > = connector.connector.get_connector_integration();
                let resp = services::execute_connector_processing_step(
                    state,
                    connector_integration,
                    self,
                    call_connector_action,
                    None,
                    None,
                )
                .await
                .to_payment_failed_response()?;

                Ok(resp)
            }
            api::GetToken::AmazonPayMetadata => create_amazon_pay_session_token(self, state).await,
        }
    }
}


pub struct NetworkTokenPaymentInformation {
    tokenized_card: NetworkTokenizedCard,
}


pub struct MpgsPaymentsResponse {
    status: MpgsPaymentStatus,
    id: String,
}


pub struct IterMut<'a, K, V> {
    inner: iter::Enumerate<slice::IterMut<'a, V>>,
    _marker: PhantomData<K>,
}


File: crates/diesel_models/src/generic_link.rs
 Public functions: 2
 Public structs: 7

use common_utils::{
    consts,
    link_utils::{
        EnabledPaymentMethod, GenericLinkStatus, GenericLinkUiConfig, PaymentMethodCollectStatus,
        PayoutLinkData, PayoutLinkStatus,
    },
};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use masking::Secret;
use serde::{Deserialize, Serialize};
use time::{Duration, PrimitiveDateTime};

use crate::{enums as storage_enums, schema::generic_link};

#[derive(
    Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize,
)]
#[diesel(table_name = generic_link, primary_key(link_id), check_for_backend(diesel::pg::Pg))]
pub struct GenericLink {
    pub link_id: String,
    pub primary_reference: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_modified_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expiry: PrimitiveDateTime,
    pub link_data: serde_json::Value,
    pub link_status: GenericLinkStatus,
    pub link_type: storage_enums::GenericLinkType,
    pub url: Secret<String>,
    pub return_url: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GenericLinkState {
    pub link_id: String,
    pub primary_reference: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_modified_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expiry: PrimitiveDateTime,
    pub link_data: GenericLinkData,
    pub link_status: GenericLinkStatus,
    pub link_type: storage_enums::GenericLinkType,
    pub url: Secret<String>,
    pub return_url: Option<String>,
}

#[derive(
    Clone,
    Debug,
    Eq,
    PartialEq,
    Insertable,
    serde::Serialize,
    serde::Deserialize,
    router_derive::DebugAsDisplay,
)]
#[diesel(table_name = generic_link)]
pub struct GenericLinkNew {
    pub link_id: String,
    pub primary_reference: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub created_at: Option<PrimitiveDateTime>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub last_modified_at: Option<PrimitiveDateTime>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expiry: PrimitiveDateTime,
    pub link_data: serde_json::Value,
    pub link_status: GenericLinkStatus,
    pub link_type: storage_enums::GenericLinkType,
    pub url: Secret<String>,
    pub return_url: Option<String>,
}

impl Default for GenericLinkNew {
    fn default() -> Self {
        let now = common_utils::date_time::now();

        Self {
            link_id: String::default(),
            primary_reference: String::default(),
            merchant_id: common_utils::id_type::MerchantId::default(),
            created_at: Some(now),
            last_modified_at: Some(now),
            expiry: now + Duration::seconds(consts::DEFAULT_SESSION_EXPIRY),
            link_data: serde_json::Value::default(),
            link_status: GenericLinkStatus::default(),
            link_type: common_enums::GenericLinkType::default(),
            url: Secret::default(),
            return_url: Option::default(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GenericLinkData {
    PaymentMethodCollect(PaymentMethodCollectLinkData),
    PayoutLink(PayoutLinkData),
}

impl GenericLinkData {
    pub fn get_payment_method_collect_data(&self) -> Result<&PaymentMethodCollectLinkData, String> {
        match self {
            Self::PaymentMethodCollect(pm) => Ok(pm),
            _ => Err("Invalid link type for fetching payment method collect data".to_string()),
        }
    }
    pub fn get_payout_link_data(&self) -> Result<&PayoutLinkData, String> {
        match self {
            Self::PayoutLink(pl) => Ok(pl),
            _ => Err("Invalid link type for fetching payout link data".to_string()),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PaymentMethodCollectLink {
    pub link_id: String,
    pub primary_reference: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_modified_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expiry: PrimitiveDateTime,
    pub link_data: PaymentMethodCollectLinkData,
    pub link_status: PaymentMethodCollectStatus,
    pub link_type: storage_enums::GenericLinkType,
    pub url: Secret<String>,
    pub return_url: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentMethodCollectLinkData {
    pub pm_collect_link_id: String,
    pub customer_id: common_utils::id_type::CustomerId,
    pub link: Secret<String>,
    pub client_secret: Secret<String>,
    pub session_expiry: u32,
    #[serde(flatten)]
    pub ui_config: GenericLinkUiConfig,
    pub enabled_payment_methods: Option<Vec<EnabledPaymentMethod>>,
}

#[derive(Clone, Debug, Identifiable, Queryable, Serialize, Deserialize)]
#[diesel(table_name = generic_link)]
#[diesel(primary_key(link_id))]
pub struct PayoutLink {
    pub link_id: String,
    pub primary_reference: common_utils::id_type::PayoutId,
    pub merchant_id: common_utils::id_type::MerchantId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_modified_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expiry: PrimitiveDateTime,
    pub link_data: PayoutLinkData,
    pub link_status: PayoutLinkStatus,
    pub link_type: storage_enums::GenericLinkType,
    pub url: Secret<String>,
    pub return_url: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PayoutLinkUpdate {
    StatusUpdate { link_status: PayoutLinkStatus },
}

#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = generic_link)]
pub struct GenericLinkUpdateInternal {
    pub link_status: Option<GenericLinkStatus>,
}

impl From<PayoutLinkUpdate> for GenericLinkUpdateInternal {
    fn from(generic_link_update: PayoutLinkUpdate) -> Self {
        match generic_link_update {
            PayoutLinkUpdate::StatusUpdate { link_status } => Self {
                link_status: Some(GenericLinkStatus::PayoutLink(link_status)),
            },
        }
    }
}


impl api::PaymentAuthorize for Novalnet {}

impl api::PaymentAuthorize for for Novalnet


impl ClickhouseClient {
    async fn execute_query(&self, query: &str) -> ClickhouseResult<Vec<serde_json::Value>> {
        logger::debug!("Executing query: {query}");
        let client = reqwest::Client::new();
        let params = CkhQuery {
            date_time_output_format: String::from("iso"),
            output_format_json_quote_64bit_integers: 0,
            database: self.database.clone(),
        };
        let response = client
            .post(&self.config.host)
            .query(&params)
            .basic_auth(self.config.username.clone(), self.config.password.clone())
            .body(format!("{query}\nFORMAT JSON"))
            .send()
            .await
            .change_context(ClickhouseError::ConnectionError)?;

        logger::debug!(clickhouse_response=?response, query=?query, "Clickhouse response");
        if response.status() != StatusCode::OK {
            response.text().await.map_or_else(
                |er| {
                    Err(ClickhouseError::ResponseError)
                        .attach_printable_lazy(|| format!("Error: {er:?}"))
                },
                |t| Err(report!(ClickhouseError::ResponseNotOK(t))),
            )
        } else {
            Ok(response
                .json::<CkhOutput<serde_json::Value>>()
                .await
                .change_context(ClickhouseError::ResponseError)?
                .data)
        }
    }
}


impl api::RefundExecute for Stax {}


pub struct TokenResponse {
    pub cnp_token: Secret<String>,
    pub token_response_code: String,
    pub token_message: String,
    #[serde(rename = "type")]
    pub card_type: Option<String>,
    pub bin: Option<String>,
}


pub async fn payment_complete(
    state: SessionState,
    req: types::DummyConnectorPaymentCompleteRequest,
) -> types::DummyConnectorResponse<()> {
    utils::tokio_mock_sleep(
        state.conf.dummy_connector.payment_duration,
        state.conf.dummy_connector.payment_tolerance,
    )
    .await;

    let payment_data = utils::get_payment_data_by_attempt_id(&state, req.attempt_id.clone()).await;

    let payment_status = if req.confirm {
        types::DummyConnectorStatus::Succeeded
    } else {
        types::DummyConnectorStatus::Failed
    };

    let redis_conn = state
        .store
        .get_redis_conn()
        .change_context(errors::DummyConnectorErrors::InternalServerError)
        .attach_printable("Failed to get redis connection")?;

    let _ = redis_conn.delete_key(&req.attempt_id.as_str().into()).await;

    if let Ok(payment_data) = payment_data {
        let updated_payment_data = types::DummyConnectorPaymentData {
            status: payment_status,
            next_action: None,
            ..payment_data
        };
        utils::store_data_in_redis(
            &state,
            updated_payment_data.payment_id.get_string_repr().to_owned(),
            updated_payment_data.clone(),
            state.conf.dummy_connector.payment_ttl,
        )
        .await?;
        return Ok(api::ApplicationResponse::JsonForRedirection(
            api_models::payments::RedirectionResponse {
                return_url: String::new(),
                params: vec![],
                return_url_with_query_params: updated_payment_data
                    .return_url
                    .unwrap_or(state.conf.dummy_connector.default_return_url.clone()),
                http_method: "GET".to_string(),
                headers: vec![],
            },
        ));
    }
    Ok(api::ApplicationResponse::JsonForRedirection(
        api_models::payments::RedirectionResponse {
            return_url: String::new(),
            params: vec![],
            return_url_with_query_params: state.conf.dummy_connector.default_return_url.clone(),
            http_method: "GET".to_string(),
            headers: vec![],
        },
    ))
}


pub fn new(
        id: id_type::OrganizationId,
        organization_type: common_enums::OrganizationType,
        organization_name: Option<String>,
    ) -> Self {
        Self {
            id,
            organization_name,
            organization_details: None,
            metadata: None,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            version: common_types::consts::API_VERSION,
            organization_type,
            platform_merchant_id: None,
        }
    }


pub async fn delete_decision_manager_config(
    state: web::Data<AppState>,
    req: HttpRequest,
) -> impl Responder {
    let flow = Flow::DecisionManagerDeleteConfig;
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        (),
        |state, auth: auth::AuthenticationData, (), _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            conditional_config::delete_conditional_config(state, merchant_context)
        },
        #[cfg(not(feature = "release"))]
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::MerchantThreeDsDecisionManagerWrite,
            },
            req.headers(),
        ),
        #[cfg(feature = "release")]
        &auth::JWTAuth {
            permission: Permission::MerchantThreeDsDecisionManagerWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl ConnectorValidation for Paytm {
    fn validate_mandate_payment(
        &self,
        _pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        match pm_data {
            PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented(
                "validate_mandate_payment does not support cards".to_string(),
            )
            .into()),
            _ => Ok(()),
        }
    }

    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        Ok(())
    }
}


pub fn list_initial_webhook_delivery_attempts() {}


pub struct CommonMandateReference {
    pub payments: Option<PaymentsMandateReference>,
    pub payouts: Option<PayoutsMandateReference>,
}


impl super::KafkaMessage for KafkaRefundEvent<'_> {
    fn key(&self) -> String {
        format!(
            "{}_{}_{}_{}",
            self.merchant_id.get_string_repr(),
            self.payment_id.get_string_repr(),
            self.attempt_id.get_string_repr(),
            self.merchant_reference_id.get_string_repr()
        )
    }
    fn event_type(&self) -> events::EventType {
        events::EventType::Refund
    }
}

impl super::KafkaMessage for for KafkaRefundEvent


pub struct PaymentsRequest {
    #[serde(rename = "DATEQ")]
    pub date: String,

    #[serde(rename = "TYPE")]
    pub transaction_type: String,

    #[serde(rename = "NUMQUESTION")]
    pub paybox_request_number: String,

    #[serde(rename = "MONTANT")]
    pub amount: MinorUnit,

    #[serde(rename = "REFERENCE")]
    pub description_reference: String,

    #[serde(rename = "VERSION")]
    pub version: String,

    #[serde(rename = "DEVISE")]
    pub currency: String,

    #[serde(rename = "PORTEUR")]
    pub card_number: cards::CardNumber,

    #[serde(rename = "DATEVAL")]
    pub expiration_date: Secret<String>,

    #[serde(rename = "CVV")]
    pub cvv: Secret<String>,

    #[serde(rename = "ACTIVITE")]
    pub activity: String,

    #[serde(rename = "SITE")]
    pub site: Secret<String>,

    #[serde(rename = "RANG")]
    pub rank: Secret<String>,

    #[serde(rename = "CLE")]
    pub key: Secret<String>,

    #[serde(rename = "ID3D")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub three_ds_data: Option<Secret<String>>,

    #[serde(rename = "REFABONNE")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub customer_id: Option<Secret<String>>,
}


pub(crate) fn create_grpc_request<T: Debug>(message: T, headers: GrpcHeaders) -> tonic::Request<T> {
    let mut request = tonic::Request::new(message);
    request.add_headers_to_grpc_request(headers);

    logger::info!(?request);

    request
}