// File: crates/router/src/db/merchant_key_store.rs
// Module: router

use common_utils::types::keymanager::KeyManagerState;
use error_stack::{report, ResultExt};
use masking::Secret;
use router_env::{instrument, tracing};
#[cfg(feature = "accounts_cache")]
use storage_impl::redis::cache::{self, CacheKind, ACCOUNTS_CACHE};

use crate::{
    connection,
    core::errors::{self, CustomResult},
    db::MockDb,
    services::Store,
    types::domain::{
        self,
        behaviour::{Conversion, ReverseConversion},
    },
};

#[async_trait::async_trait]
pub trait MerchantKeyStoreInterface {
    async fn insert_merchant_key_store(
        &self,
        state: &KeyManagerState,
        merchant_key_store: domain::MerchantKeyStore,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::MerchantKeyStore, errors::StorageError>;

    async fn get_merchant_key_store_by_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::MerchantKeyStore, errors::StorageError>;

    async fn delete_merchant_key_store_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError>;

    #[cfg(feature = "olap")]
    async fn list_multiple_key_stores(
        &self,
        state: &KeyManagerState,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<Vec<domain::MerchantKeyStore>, errors::StorageError>;

    async fn get_all_key_stores(
        &self,
        state: &KeyManagerState,
        key: &Secret<Vec<u8>>,
        from: u32,
        to: u32,
    ) -> CustomResult<Vec<domain::MerchantKeyStore>, errors::StorageError>;
}

#[async_trait::async_trait]
impl MerchantKeyStoreInterface for Store {
    #[instrument(skip_all)]
    async fn insert_merchant_key_store(
        &self,
        state: &KeyManagerState,
        merchant_key_store: domain::MerchantKeyStore,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::MerchantKeyStore, errors::StorageError> {
        let conn = connection::pg_accounts_connection_write(self).await?;
        let merchant_id = merchant_key_store.merchant_id.clone();
        merchant_key_store
            .construct_new()
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))?
            .convert(state, key, merchant_id.into())
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[instrument(skip_all)]
    async fn get_merchant_key_store_by_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::MerchantKeyStore, errors::StorageError> {
        let fetch_func = || async {
            let conn = connection::pg_accounts_connection_read(self).await?;

            diesel_models::merchant_key_store::MerchantKeyStore::find_by_merchant_id(
                &conn,
                merchant_id,
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
        };

        #[cfg(not(feature = "accounts_cache"))]
        {
            fetch_func()
                .await?
                .convert(state, key, merchant_id.clone().into())
                .await
                .change_context(errors::StorageError::DecryptionError)
        }

        #[cfg(feature = "accounts_cache")]
        {
            let key_store_cache_key =
                format!("merchant_key_store_{}", merchant_id.get_string_repr());
            cache::get_or_populate_in_memory(
                self,
                &key_store_cache_key,
                fetch_func,
                &ACCOUNTS_CACHE,
            )
            .await?
            .convert(state, key, merchant_id.clone().into())
            .await
            .change_context(errors::StorageError::DecryptionError)
        }
    }

    #[instrument(skip_all)]
    async fn delete_merchant_key_store_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        let delete_func = || async {
            let conn = connection::pg_accounts_connection_write(self).await?;
            diesel_models::merchant_key_store::MerchantKeyStore::delete_by_merchant_id(
                &conn,
                merchant_id,
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
        };

        #[cfg(not(feature = "accounts_cache"))]
        {
            delete_func().await
        }

        #[cfg(feature = "accounts_cache")]
        {
            let key_store_cache_key =
                format!("merchant_key_store_{}", merchant_id.get_string_repr());
            cache::publish_and_redact(
                self,
                CacheKind::Accounts(key_store_cache_key.into()),
                delete_func,
            )
            .await
        }
    }

    #[cfg(feature = "olap")]
    #[instrument(skip_all)]
    async fn list_multiple_key_stores(
        &self,
        state: &KeyManagerState,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<Vec<domain::MerchantKeyStore>, errors::StorageError> {
        let fetch_func = || async {
            let conn = connection::pg_accounts_connection_read(self).await?;

            diesel_models::merchant_key_store::MerchantKeyStore::list_multiple_key_stores(
                &conn,
                merchant_ids,
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
        };

        futures::future::try_join_all(fetch_func().await?.into_iter().map(|key_store| async {
            let merchant_id = key_store.merchant_id.clone();
            key_store
                .convert(state, key, merchant_id.into())
                .await
                .change_context(errors::StorageError::DecryptionError)
        }))
        .await
    }

    async fn get_all_key_stores(
        &self,
        state: &KeyManagerState,
        key: &Secret<Vec<u8>>,
        from: u32,
        to: u32,
    ) -> CustomResult<Vec<domain::MerchantKeyStore>, errors::StorageError> {
        let conn = connection::pg_accounts_connection_read(self).await?;
        let stores = diesel_models::merchant_key_store::MerchantKeyStore::list_all_key_stores(
            &conn, from, to,
        )
        .await
        .map_err(|err| report!(errors::StorageError::from(err)))?;

        futures::future::try_join_all(stores.into_iter().map(|key_store| async {
            let merchant_id = key_store.merchant_id.clone();
            key_store
                .convert(state, key, merchant_id.into())
                .await
                .change_context(errors::StorageError::DecryptionError)
        }))
        .await
    }
}

#[async_trait::async_trait]
impl MerchantKeyStoreInterface for MockDb {
    async fn insert_merchant_key_store(
        &self,
        state: &KeyManagerState,
        merchant_key_store: domain::MerchantKeyStore,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::MerchantKeyStore, errors::StorageError> {
        let mut locked_merchant_key_store = self.merchant_key_store.lock().await;

        if locked_merchant_key_store
            .iter()
            .any(|merchant_key| merchant_key.merchant_id == merchant_key_store.merchant_id)
        {
            Err(errors::StorageError::DuplicateValue {
                entity: "merchant_key_store",
                key: Some(merchant_key_store.merchant_id.get_string_repr().to_owned()),
            })?;
        }

        let merchant_key = Conversion::convert(merchant_key_store)
            .await
            .change_context(errors::StorageError::MockDbError)?;
        locked_merchant_key_store.push(merchant_key.clone());
        let merchant_id = merchant_key.merchant_id.clone();
        merchant_key
            .convert(state, key, merchant_id.into())
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn get_merchant_key_store_by_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &common_utils::id_type::MerchantId,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::MerchantKeyStore, errors::StorageError> {
        self.merchant_key_store
            .lock()
            .await
            .iter()
            .find(|merchant_key| merchant_key.merchant_id == *merchant_id)
            .cloned()
            .ok_or(errors::StorageError::ValueNotFound(String::from(
                "merchant_key_store",
            )))?
            .convert(state, key, merchant_id.clone().into())
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    async fn delete_merchant_key_store_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        let mut merchant_key_stores = self.merchant_key_store.lock().await;
        let index = merchant_key_stores
            .iter()
            .position(|mks| mks.merchant_id == *merchant_id)
            .ok_or(errors::StorageError::ValueNotFound(format!(
                "No merchant key store found for merchant_id = {merchant_id:?}",
            )))?;
        merchant_key_stores.remove(index);
        Ok(true)
    }

    #[cfg(feature = "olap")]
    async fn list_multiple_key_stores(
        &self,
        state: &KeyManagerState,
        merchant_ids: Vec<common_utils::id_type::MerchantId>,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<Vec<domain::MerchantKeyStore>, errors::StorageError> {
        let merchant_key_stores = self.merchant_key_store.lock().await;
        futures::future::try_join_all(
            merchant_key_stores
                .iter()
                .filter(|merchant_key| merchant_ids.contains(&merchant_key.merchant_id))
                .map(|merchant_key| async {
                    merchant_key
                        .to_owned()
                        .convert(state, key, merchant_key.merchant_id.clone().into())
                        .await
                        .change_context(errors::StorageError::DecryptionError)
                }),
        )
        .await
    }
    async fn get_all_key_stores(
        &self,
        state: &KeyManagerState,
        key: &Secret<Vec<u8>>,
        _from: u32,
        _to: u32,
    ) -> CustomResult<Vec<domain::MerchantKeyStore>, errors::StorageError> {
        let merchant_key_stores = self.merchant_key_store.lock().await;

        futures::future::try_join_all(merchant_key_stores.iter().map(|merchant_key| async {
            merchant_key
                .to_owned()
                .convert(state, key, merchant_key.merchant_id.clone().into())
                .await
                .change_context(errors::StorageError::DecryptionError)
        }))
        .await
    }
}

#[cfg(test)]
mod tests {
    use std::{borrow::Cow, sync::Arc};

    use common_utils::{type_name, types::keymanager::Identifier};
    use time::macros::datetime;
    use tokio::sync::oneshot;

    use crate::{
        db::{merchant_key_store::MerchantKeyStoreInterface, MasterKeyInterface, MockDb},
        routes::{
            self,
            app::{settings::Settings, StorageImpl},
        },
        services,
        types::domain,
    };

    #[allow(clippy::unwrap_used, clippy::expect_used)]
    #[tokio::test]
    async fn test_mock_db_merchant_key_store_interface() {
        let conf = Settings::new().expect("invalid settings");
        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 mock_db = MockDb::new(&redis_interface::RedisSettings::default())
            .await
            .expect("Failed to create mock DB");
        let master_key = mock_db.get_master_key();
        let merchant_id =
            common_utils::id_type::MerchantId::try_from(Cow::from("merchant1")).unwrap();
        let identifier = Identifier::Merchant(merchant_id.clone());
        let key_manager_state = &state.into();
        let merchant_key1 = mock_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.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 found_merchant_key1 = mock_db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_id,
                &master_key.to_vec().into(),
            )
            .await
            .unwrap();

        assert_eq!(found_merchant_key1.merchant_id, merchant_key1.merchant_id);
        assert_eq!(found_merchant_key1.key, merchant_key1.key);

        let insert_duplicate_merchant_key1_result = mock_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.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;
        assert!(insert_duplicate_merchant_key1_result.is_err());

        let non_existent_merchant_id =
            common_utils::id_type::MerchantId::try_from(Cow::from("non_existent")).unwrap();

        let find_non_existent_merchant_key_result = mock_db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &non_existent_merchant_id,
                &master_key.to_vec().into(),
            )
            .await;
        assert!(find_non_existent_merchant_key_result.is_err());

        let find_merchant_key_with_incorrect_master_key_result = mock_db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_id,
                &vec![0; 32].into(),
            )
            .await;
        assert!(find_merchant_key_with_incorrect_master_key_result.is_err());
    }
}


                let val = secret_val
                    .into_iter()
                    .map(|secret| secret.expose())
                    .collect();
                serde_json::Value::Array(val)
                    .parse_value("PaymentMethods")
                    .change_context(errors::ApiErrorResponse::InternalServerError)?
            }
            None => None,
        };
        let frm_configs = match item.frm_configs {
            Some(ref frm_value) => {
                let configs_for_frm : Vec<api_models::admin::FrmConfigs> = frm_value
                    .iter()
                    .map(|config| { config
                        .peek()
                        .clone()
                        .parse_value("FrmConfigs")
                        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                            field_name: "frm_configs".to_string(),
                            expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","payment_method_types": [{"payment_method_type": "credit","card_networks": ["Visa"],"flow": "pre","action": "cancel_txn"}]}]}]"#.to_string(),
                        })
                    })
                    .collect::<Result<Vec<_>, _>>()?;
                Some(configs_for_frm)
            }
            None => None,
        };
        // parse the connector_account_details into ConnectorAuthType
        let connector_account_details: hyperswitch_domain_models::router_data::ConnectorAuthType =
            item.connector_account_details
                .clone()
                .into_inner()
                .parse_value("ConnectorAuthType")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed while parsing value for ConnectorAuthType")?;
        // get the masked keys from the ConnectorAuthType and encode it to secret value
        let masked_connector_account_details = Secret::new(
            connector_account_details
                .get_masked_keys()
                .encode_to_value()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to encode ConnectorAuthType")?,
        );
        #[cfg(feature = "v2")]
        let response = Self {
            id: item.get_id(),
            connector_type: item.connector_type,
            connector_name: item.connector_name,
            connector_label: item.connector_label,
            connector_account_details: masked_connector_account_details,
            disabled: item.disabled,
            payment_methods_enabled,
            metadata: item.metadata,
            frm_configs,
            connector_webhook_details: item
                .connector_webhook_details
                .map(|webhook_details| {
                    serde_json::Value::parse_value(
                        webhook_details.expose(),
                        "MerchantConnectorWebhookDetails",
                    )
                    .attach_printable("Unable to deserialize connector_webhook_details")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                })
                .transpose()?,
            profile_id: item.profile_id,
            applepay_verified_domains: item.applepay_verified_domains,
            pm_auth_config: item.pm_auth_config,
            status: item.status,
            additional_merchant_data: item
                .additional_merchant_data
                .map(|data| {
                    let data = data.into_inner();
                    serde_json::Value::parse_value::<router_types::AdditionalMerchantData>(
                        data.expose(),
                        "AdditionalMerchantData",
                    )
                    .attach_printable("Unable to deserialize additional_merchant_data")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                })
                .transpose()?
                .map(api_models::admin::AdditionalMerchantData::foreign_from),
            connector_wallets_details: item
                .connector_wallets_details
                .map(|data| {
                    data.into_inner()
                        .expose()
                        .parse_value::<api_models::admin::ConnectorWalletDetails>(
                            "ConnectorWalletDetails",
                        )
                        .attach_printable("Unable to deserialize connector_wallets_details")
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                })
                .transpose()?,
        };
        #[cfg(feature = "v1")]
        let response = Self {
            connector_type: item.connector_type,
            connector_name: item.connector_name,
            connector_label: item.connector_label,
            merchant_connector_id: item.merchant_connector_id,
            connector_account_details: masked_connector_account_details,
            test_mode: item.test_mode,
            disabled: item.disabled,
            payment_methods_enabled,
            metadata: item.metadata,
            business_country: item.business_country,
            business_label: item.business_label,
            business_sub_label: item.business_sub_label,
            frm_configs,
            connector_webhook_details: item
                .connector_webhook_details
                .map(|webhook_details| {
                    serde_json::Value::parse_value(
                        webhook_details.expose(),
                        "MerchantConnectorWebhookDetails",
                    )
                    .attach_printable("Unable to deserialize connector_webhook_details")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                })
                .transpose()?,
            profile_id: item.profile_id,
            applepay_verified_domains: item.applepay_verified_domains,
            pm_auth_config: item.pm_auth_config,
            status: item.status,
            additional_merchant_data: item
                .additional_merchant_data
                .map(|data| {
                    let data = data.into_inner();
                    serde_json::Value::parse_value::<router_types::AdditionalMerchantData>(
                        data.expose(),
                        "AdditionalMerchantData",
                    )
                    .attach_printable("Unable to deserialize additional_merchant_data")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                })
                .transpose()?
                .map(api_models::admin::AdditionalMerchantData::foreign_from),
            connector_wallets_details: item
                .connector_wallets_details
                .map(|data| {
                    data.into_inner()
                        .expose()
                        .parse_value::<api_models::admin::ConnectorWalletDetails>(
                            "ConnectorWalletDetails",
                        )
                        .attach_printable("Unable to deserialize connector_wallets_details")
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                })
                .transpose()?,
        };
        Ok(response)
    }
}

#[cfg(feature = "v2")]
impl ForeignTryFrom<domain::MerchantConnectorAccount>
    for api_models::admin::MerchantConnectorResponse
{
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(item: domain::MerchantConnectorAccount) -> Result<Self, Self::Error> {
        let frm_configs = match item.frm_configs {
            Some(ref frm_value) => {
                let configs_for_frm : Vec<api_models::admin::FrmConfigs> = frm_value
                    .iter()
                    .map(|config| { config
                        .peek()
                        .clone()
                        .parse_value("FrmConfigs")
                        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                            field_name: "frm_configs".to_string(),
                            expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","payment_method_types": [{"payment_method_type": "credit","card_networks": ["Visa"],"flow": "pre","action": "cancel_txn"}]}]}]"#.to_string(),
                        })
                    })
                    .collect::<Result<Vec<_>, _>>()?;
                Some(configs_for_frm)
            }
            None => None,
        };

        // parse the connector_account_details into ConnectorAuthType
        let connector_account_details: hyperswitch_domain_models::router_data::ConnectorAuthType =
            item.connector_account_details
                .clone()
                .into_inner()
                .parse_value("ConnectorAuthType")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed while parsing value for ConnectorAuthType")?;
        // get the masked keys from the ConnectorAuthType and encode it to secret value
        let masked_connector_account_details = Secret::new(
            connector_account_details
                .get_masked_keys()
                .encode_to_value()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to encode ConnectorAuthType")?,
        );

        let feature_metadata = item.feature_metadata.as_ref().map(|metadata| {
            api_models::admin::MerchantConnectorAccountFeatureMetadata::foreign_from(metadata)
        });

        let response = Self {
            id: item.get_id(),
            connector_type: item.connector_type,
            connector_name: item.connector_name,
            connector_label: item.connector_label,
            connector_account_details: masked_connector_account_details,
            disabled: item.disabled,
            payment_methods_enabled: item.payment_methods_enabled,
            metadata: item.metadata,
            frm_configs,
            connector_webhook_details: item
                .connector_webhook_details
                .map(|webhook_details| {
                    serde_json::Value::parse_value(
                        webhook_details.expose(),
                        "MerchantConnectorWebhookDetails",
                    )
                    .attach_printable("Unable to deserialize connector_webhook_details")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                })
                .transpose()?,
            profile_id: item.profile_id,
            applepay_verified_domains: item.applepay_verified_domains,
            pm_auth_config: item.pm_auth_config,
            status: item.status,
            additional_merchant_data: item
                .additional_merchant_data
                .map(|data| {
                    let data = data.into_inner();
                    serde_json::Value::parse_value::<router_types::AdditionalMerchantData>(
                        data.expose(),
                        "AdditionalMerchantData",
                    )
                    .attach_printable("Unable to deserialize additional_merchant_data")
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                })
                .transpose()?
                .map(api_models::admin::AdditionalMerchantData::foreign_from),
            connector_wallets_details: item
                .connector_wallets_details
                .map(|data| {
                    data.into_inner()
                        .expose()
                        .parse_value::<api_models::admin::ConnectorWalletDetails>(
                            "ConnectorWalletDetails",
                        )
                        .attach_printable("Unable to deserialize connector_wallets_details")
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                })
                .transpose()?,
            feature_metadata,
        };
        Ok(response)
    }
}

#[cfg(feature = "v1")]
impl ForeignFrom<storage::PaymentAttempt> for payments::PaymentAttemptResponse {
    fn foreign_from(payment_attempt: storage::PaymentAttempt) -> Self {
        let connector_transaction_id = payment_attempt
            .get_connector_payment_id()
            .map(ToString::to_string);
        Self {
            attempt_id: payment_attempt.attempt_id,
            status: payment_attempt.status,
            amount: payment_attempt.net_amount.get_order_amount(),
            order_tax_amount: payment_attempt.net_amount.get_order_tax_amount(),
            currency: payment_attempt.currency,
            connector: payment_attempt.connector,
            error_message: payment_attempt.error_reason,
            payment_method: payment_attempt.payment_method,
            connector_transaction_id,
            capture_method: payment_attempt.capture_method,
            authentication_type: payment_attempt.authentication_type,
            created_at: payment_attempt.created_at,
            modified_at: payment_attempt.modified_at,
            cancellation_reason: payment_attempt.cancellation_reason,
            mandate_id: payment_attempt.mandate_id,
            error_code: payment_attempt.error_code,
            payment_token: payment_attempt.payment_token,
            connector_metadata: payment_attempt.connector_metadata,
            payment_experience: payment_attempt.payment_experience,
            payment_method_type: payment_attempt.payment_method_type,
            reference_id: payment_attempt.connector_response_reference_id,
            unified_code: payment_attempt.unified_code,
            unified_message: payment_attempt.unified_message,
            client_source: payment_attempt.client_source,
            client_version: payment_attempt.client_version,
        }
    }
}

impl ForeignFrom<storage::Capture> for payments::CaptureResponse {
    fn foreign_from(capture: storage::Capture) -> Self {
        let connector_capture_id = capture.get_optional_connector_transaction_id().cloned();
        Self {
            capture_id: capture.capture_id,
            status: capture.status,
            amount: capture.amount,
            currency: capture.currency,
            connector: capture.connector,
            authorized_attempt_id: capture.authorized_attempt_id,
            connector_capture_id,
            capture_sequence: capture.capture_sequence,
            error_message: capture.error_message,
            error_code: capture.error_code,
            error_reason: capture.error_reason,
            reference_id: capture.connector_response_reference_id,
        }
    }
}

#[cfg(feature = "payouts")]
impl ForeignFrom<&api_models::payouts::PayoutMethodData> for api_enums::PaymentMethodType {
    fn foreign_from(value: &api_models::payouts::PayoutMethodData) -> Self {
        match value {
            api_models::payouts::PayoutMethodData::Bank(bank) => Self::foreign_from(bank),
            api_models::payouts::PayoutMethodData::Card(_) => Self::Debit,
            api_models::payouts::PayoutMethodData::Wallet(wallet) => Self::foreign_from(wallet),
        }
    }
}

#[cfg(feature = "payouts")]
impl ForeignFrom<&api_models::payouts::Bank> for api_enums::PaymentMethodType {
    fn foreign_from(value: &api_models::payouts::Bank) -> Self {
        match value {
            api_models::payouts::Bank::Ach(_) => Self::Ach,
            api_models::payouts::Bank::Bacs(_) => Self::Bacs,
            api_models::payouts::Bank::Sepa(_) => Self::SepaBankTransfer,
            api_models::payouts::Bank::Pix(_) => Self::Pix,
        }
    }
}

#[cfg(feature = "payouts")]
impl ForeignFrom<&api_models::payouts::Wallet> for api_enums::PaymentMethodType {
    fn foreign_from(value: &api_models::payouts::Wallet) -> Self {
        match value {
            api_models::payouts::Wallet::Paypal(_) => Self::Paypal,
            api_models::payouts::Wallet::Venmo(_) => Self::Venmo,
        }
    }
}

#[cfg(feature = "payouts")]
impl ForeignFrom<&api_models::payouts::PayoutMethodData> for api_enums::PaymentMethod {
    fn foreign_from(value: &api_models::payouts::PayoutMethodData) -> Self {
        match value {
            api_models::payouts::PayoutMethodData::Bank(_) => Self::BankTransfer,
            api_models::payouts::PayoutMethodData::Card(_) => Self::Card,
            api_models::payouts::PayoutMethodData::Wallet(_) => Self::Wallet,
        }
    }
}

#[cfg(feature = "payouts")]
impl ForeignFrom<&api_models::payouts::PayoutMethodData> for api_models::enums::PayoutType {
    fn foreign_from(value: &api_models::payouts::PayoutMethodData) -> Self {
        match value {
            api_models::payouts::PayoutMethodData::Bank(_) => Self::Bank,
            api_models::payouts::PayoutMethodData::Card(_) => Self::Card,
            api_models::payouts::PayoutMethodData::Wallet(_) => Self::Wallet,
        }
    }
}

#[cfg(feature = "payouts")]
impl ForeignFrom<api_models::enums::PayoutType> for api_enums::PaymentMethod {
    fn foreign_from(value: api_models::enums::PayoutType) -> Self {
        match value {
            api_models::enums::PayoutType::Bank => Self::BankTransfer,
            api_models::enums::PayoutType::Card => Self::Card,
            api_models::enums::PayoutType::Wallet => Self::Wallet,
        }
    }
}

#[cfg(feature = "payouts")]
impl ForeignTryFrom<api_enums::PaymentMethod> for api_models::enums::PayoutType {
    type Error = error_stack::Report<errors::ApiErrorResponse>;

    fn foreign_try_from(value: api_enums::PaymentMethod) -> Result<Self, Self::Error> {
        match value {
            api_enums::PaymentMethod::Card => Ok(Self::Card),
            api_enums::PaymentMethod::BankTransfer => Ok(Self::Bank),
            api_enums::PaymentMethod::Wallet => Ok(Self::Wallet),
            _ => Err(errors::ApiErrorResponse::InvalidRequestData {
                message: format!("PaymentMethod {value:?} is not supported for payouts"),
            })
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to convert PaymentMethod to PayoutType"),
        }
    }
}

#[cfg(feature = "v1")]
impl ForeignTryFrom<&HeaderMap> for hyperswitch_domain_models::payments::HeaderPayload {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(headers: &HeaderMap) -> Result<Self, Self::Error> {
        let payment_confirm_source: Option<api_enums::PaymentSource> =
            get_header_value_by_key(X_PAYMENT_CONFIRM_SOURCE.into(), headers)?
                .map(|source| {
                    source
                        .to_owned()
                        .parse_enum("PaymentSource")
                        .change_context(errors::ApiErrorResponse::InvalidRequestData {
                            message: "Invalid data received in payment_confirm_source header"
                                .into(),
                        })
                        .attach_printable(
                            "Failed while paring PaymentConfirmSource header value to enum",
                        )
                })
                .transpose()?;
        when(
            payment_confirm_source.is_some_and(|payment_confirm_source| {
                payment_confirm_source.is_for_internal_use_only()
            }),
            || {
                Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Invalid data received in payment_confirm_source header".into(),
                }))
            },
        )?;
        let locale =
            get_header_value_by_key(ACCEPT_LANGUAGE.into(), headers)?.map(|val| val.to_string());
        let x_hs_latency = get_header_value_by_key(X_HS_LATENCY.into(), headers)
            .map(|value| value == Some("true"))
            .unwrap_or(false);

        let client_source =
            get_header_value_by_key(X_CLIENT_SOURCE.into(), headers)?.map(|val| val.to_string());

        let client_version =
            get_header_value_by_key(X_CLIENT_VERSION.into(), headers)?.map(|val| val.to_string());

        let browser_name_str =
            get_header_value_by_key(BROWSER_NAME.into(), headers)?.map(|val| val.to_string());

        let browser_name: Option<api_enums::BrowserName> = browser_name_str.map(|browser_name| {
            browser_name
                .parse_enum("BrowserName")
                .unwrap_or(api_enums::BrowserName::Unknown)
        });

        let x_client_platform_str =
            get_header_value_by_key(X_CLIENT_PLATFORM.into(), headers)?.map(|val| val.to_string());

        let x_client_platform: Option<api_enums::ClientPlatform> =
            x_client_platform_str.map(|x_client_platform| {
                x_client_platform
                    .parse_enum("ClientPlatform")
                    .unwrap_or(api_enums::ClientPlatform::Unknown)
            });

        let x_merchant_domain =
            get_header_value_by_key(X_MERCHANT_DOMAIN.into(), headers)?.map(|val| val.to_string());

        let x_app_id =
            get_header_value_by_key(X_APP_ID.into(), headers)?.map(|val| val.to_string());

        let x_redirect_uri =
            get_header_value_by_key(X_REDIRECT_URI.into(), headers)?.map(|val| val.to_string());
        let x_reference_id =
            get_header_value_by_key(X_REFERENCE_ID.into(), headers)?.map(|val| val.to_string());

        Ok(Self {
            payment_confirm_source,
            client_source,
            client_version,
            x_hs_latency: Some(x_hs_latency),
            browser_name,
            x_client_platform,
            x_merchant_domain,
            locale,
            x_app_id,
            x_redirect_uri,
            x_reference_id,
        })
    }
}

#[cfg(feature = "v2")]
impl ForeignTryFrom<&HeaderMap> for hyperswitch_domain_models::payments::HeaderPayload {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(headers: &HeaderMap) -> Result<Self, Self::Error> {
        use std::str::FromStr;

        use crate::headers::X_CLIENT_SECRET;

        let payment_confirm_source: Option<api_enums::PaymentSource> =
            get_header_value_by_key(X_PAYMENT_CONFIRM_SOURCE.into(), headers)?
                .map(|source| {
                    source
                        .to_owned()
                        .parse_enum("PaymentSource")
                        .change_context(errors::ApiErrorResponse::InvalidRequestData {
                            message: "Invalid data received in payment_confirm_source header"
                                .into(),
                        })
                        .attach_printable(
                            "Failed while paring PaymentConfirmSource header value to enum",
                        )
                })
                .transpose()?;
        when(
            payment_confirm_source.is_some_and(|payment_confirm_source| {
                payment_confirm_source.is_for_internal_use_only()
            }),
            || {
                Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                    message: "Invalid data received in payment_confirm_source header".into(),
                }))
            },
        )?;
        let locale =
            get_header_value_by_key(ACCEPT_LANGUAGE.into(), headers)?.map(|val| val.to_string());
        let x_hs_latency = get_header_value_by_key(X_HS_LATENCY.into(), headers)
            .map(|value| value == Some("true"))
            .unwrap_or(false);

        let client_source =
            get_header_value_by_key(X_CLIENT_SOURCE.into(), headers)?.map(|val| val.to_string());

        let client_version =
            get_header_value_by_key(X_CLIENT_VERSION.into(), headers)?.map(|val| val.to_string());

        let browser_name_str =
            get_header_value_by_key(BROWSER_NAME.into(), headers)?.map(|val| val.to_string());

        let browser_name: Option<api_enums::BrowserName> = browser_name_str.map(|browser_name| {
            browser_name
                .parse_enum("BrowserName")
                .unwrap_or(api_enums::BrowserName::Unknown)
        });

        let x_client_platform_str =
            get_header_value_by_key(X_CLIENT_PLATFORM.into(), headers)?.map(|val| val.to_string());

        let x_client_platform: Option<api_enums::ClientPlatform> =
            x_client_platform_str.map(|x_client_platform| {
                x_client_platform
                    .parse_enum("ClientPlatform")
                    .unwrap_or(api_enums::ClientPlatform::Unknown)
            });

        let x_merchant_domain =
            get_header_value_by_key(X_MERCHANT_DOMAIN.into(), headers)?.map(|val| val.to_string());

        let x_app_id =
            get_header_value_by_key(X_APP_ID.into(), headers)?.map(|val| val.to_string());

        let x_redirect_uri =
            get_header_value_by_key(X_REDIRECT_URI.into(), headers)?.map(|val| val.to_string());
        let x_reference_id =
            get_header_value_by_key(X_REFERENCE_ID.into(), headers)?.map(|val| val.to_string());

        Ok(Self {
            payment_confirm_source,
            // client_source,
            // client_version,
            x_hs_latency: Some(x_hs_latency),
            browser_name,
            x_client_platform,
            x_merchant_domain,
            locale,
            x_app_id,
            x_redirect_uri,
            x_reference_id,
        })
    }
}

#[cfg(feature = "v1")]
impl
    ForeignTryFrom<(
        Option<&storage::PaymentAttempt>,
        Option<&storage::PaymentIntent>,
        Option<&domain::Address>,
        Option<&domain::Address>,
        Option<&domain::Customer>,
    )> for payments::PaymentsRequest
{
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(
        value: (
            Option<&storage::PaymentAttempt>,
            Option<&storage::PaymentIntent>,
            Option<&domain::Address>,
            Option<&domain::Address>,
            Option<&domain::Customer>,
        ),
    ) -> Result<Self, Self::Error> {
        let (payment_attempt, payment_intent, shipping, billing, customer) = value;
        // Populating the dynamic fields directly, for the cases where we have customer details stored in
        // Payment Intent
        let customer_details_from_pi = payment_intent
            .and_then(|payment_intent| payment_intent.customer_details.clone())
            .map(|customer_details| {
                customer_details
                    .into_inner()
                    .peek()
                    .clone()
                    .parse_value::<CustomerData>("CustomerData")
                    .change_context(errors::ApiErrorResponse::InvalidDataValue {
                        field_name: "customer_details",
                    })
                    .attach_printable("Failed to parse customer_details")
            })
            .transpose()
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: "customer_details",
            })?;

        let mut billing_address = billing
            .map(hyperswitch_domain_models::address::Address::from)
            .map(api_types::Address::from);

        // This change is to fix a merchant integration
        // If billing.email is not passed by the merchant, and if the customer email is present, then use the `customer.email` as the billing email
        if let Some(billing_address) = &mut billing_address {
            billing_address.email = billing_address.email.clone().or_else(|| {
                customer
                    .and_then(|cust| {
                        cust.email
                            .as_ref()
                            .map(|email| pii::Email::from(email.clone()))
                    })
                    .or(customer_details_from_pi.clone().and_then(|cd| cd.email))
            });
        } else {
            billing_address = Some(payments::Address {
                email: customer
                    .and_then(|cust| {
                        cust.email
                            .as_ref()
                            .map(|email| pii::Email::from(email.clone()))
                    })
                    .or(customer_details_from_pi.clone().and_then(|cd| cd.email)),
                ..Default::default()
            });
        }

        Ok(Self {
            currency: payment_attempt.map(|pa| pa.currency.unwrap_or_default()),
            shipping: shipping
                .map(hyperswitch_domain_models::address::Address::from)
                .map(api_types::Address::from),
            billing: billing_address,
            amount: payment_attempt
                .map(|pa| api_types::Amount::from(pa.net_amount.get_order_amount())),
            email: customer
                .and_then(|cust| cust.email.as_ref().map(|em| pii::Email::from(em.clone())))
                .or(customer_details_from_pi.clone().and_then(|cd| cd.email)),
            phone: customer
                .and_then(|cust| cust.phone.as_ref().map(|p| p.clone().into_inner()))
                .or(customer_details_from_pi.clone().and_then(|cd| cd.phone)),
            name: customer
                .and_then(|cust| cust.name.as_ref().map(|n| n.clone().into_inner()))
                .or(customer_details_from_pi.clone().and_then(|cd| cd.name)),
            ..Self::default()
        })
    }
}

impl ForeignFrom<(storage::PaymentLink, payments::PaymentLinkStatus)>
    for payments::RetrievePaymentLinkResponse
{
    fn foreign_from(
        (payment_link_config, status): (storage::PaymentLink, payments::PaymentLinkStatus),
    ) -> Self {
        Self {
            payment_link_id: payment_link_config.payment_link_id,
            merchant_id: payment_link_config.merchant_id,
            link_to_pay: payment_link_config.link_to_pay,
            amount: payment_link_config.amount,
            created_at: payment_link_config.created_at,
            expiry: payment_link_config.fulfilment_time,
            description: payment_link_config.description,
            currency: payment_link_config.currency,
            status,
            secure_link: payment_link_config.secure_link,
        }
    }
}

impl From<domain::Address> for payments::AddressDetails {
    fn from(addr: domain::Address) -> Self {
        Self {
            city: addr.city,
            country: addr.country,
            line1: addr.line1.map(Encryptable::into_inner),
            line2: addr.line2.map(Encryptable::into_inner),
            line3: addr.line3.map(Encryptable::into_inner),
            zip: addr.zip.map(Encryptable::into_inner),
            state: addr.state.map(Encryptable::into_inner),
            first_name: addr.first_name.map(Encryptable::into_inner),
            last_name: addr.last_name.map(Encryptable::into_inner),
            origin_zip: addr.origin_zip.map(Encryptable::into_inner),
        }
    }
}

impl ForeignFrom<ConnectorSelection> for routing_types::StaticRoutingAlgorithm {
    fn foreign_from(value: ConnectorSelection) -> Self {
        match value {
            ConnectorSelection::Priority(connectors) => Self::Priority(connectors),

            ConnectorSelection::VolumeSplit(splits) => Self::VolumeSplit(splits),
        }
    }
}

impl ForeignFrom<api_models::organization::OrganizationNew>
    for diesel_models::organization::OrganizationNew
{
    fn foreign_from(item: api_models::organization::OrganizationNew) -> Self {
        Self::new(item.org_id, item.org_type, item.org_name)
    }
}

impl ForeignFrom<api_models::organization::OrganizationCreateRequest>
    for diesel_models::organization::OrganizationNew
{
    fn foreign_from(item: api_models::organization::OrganizationCreateRequest) -> Self {
        // Create a new organization with a standard type by default
        let org_new = api_models::organization::OrganizationNew::new(
            common_enums::OrganizationType::Standard,
            None,
        );
        let api_models::organization::OrganizationCreateRequest {
            organization_name,
            organization_details,
            metadata,
        } = item;
        let mut org_new_db = Self::new(org_new.org_id, org_new.org_type, Some(organization_name));
        org_new_db.organization_details = organization_details;
        org_new_db.metadata = metadata;
        org_new_db
    }
}

impl ForeignFrom<gsm_api_types::GsmCreateRequest>
    for hyperswitch_domain_models::gsm::GatewayStatusMap
{
    fn foreign_from(value: gsm_api_types::GsmCreateRequest) -> Self {
        let inferred_feature_data = common_types::domain::GsmFeatureData::foreign_from(&value);
        Self {
            connector: value.connector.to_string(),
            flow: value.flow,
            sub_flow: value.sub_flow,
            code: value.code,
            message: value.message,
            status: value.status,
            router_error: value.router_error,
            unified_code: value.unified_code,
            unified_message: value.unified_message,
            error_category: value.error_category,
            feature_data: value.feature_data.unwrap_or(inferred_feature_data),
            feature: value.feature.unwrap_or(api_enums::GsmFeature::Retry),
        }
    }
}

impl ForeignFrom<&gsm_api_types::GsmCreateRequest> for common_types::domain::GsmFeatureData {
    fn foreign_from(value: &gsm_api_types::GsmCreateRequest) -> Self {
        // Defaulting alternate_network_possible to false as it is provided only in the Retry feature
        // If the retry feature is not used, we assume alternate network as false
        let alternate_network_possible = false;

        match value.feature {
            Some(api_enums::GsmFeature::Retry) | None => {
                Self::Retry(common_types::domain::RetryFeatureData {
                    step_up_possible: value.step_up_possible,
                    clear_pan_possible: value.clear_pan_possible,
                    alternate_network_possible,
                    decision: value.decision,
                })
            }
        }
    }
}

impl
    ForeignFrom<(
        &gsm_api_types::GsmUpdateRequest,
        hyperswitch_domain_models::gsm::GatewayStatusMap,
    )> for (api_enums::GsmFeature, common_types::domain::GsmFeatureData)
{
    fn foreign_from(
        (gsm_update_request, gsm_db_record): (
            &gsm_api_types::GsmUpdateRequest,
            hyperswitch_domain_models::gsm::GatewayStatusMap,
        ),
    ) -> Self {
        let gsm_db_record_inferred_feature = match gsm_db_record.feature_data.get_decision() {
            api_enums::GsmDecision::Retry | api_enums::GsmDecision::DoDefault => {
                api_enums::GsmFeature::Retry
            }
        };

        let gsm_feature = gsm_update_request
            .feature
            .unwrap_or(gsm_db_record_inferred_feature);

        match gsm_feature {
            api_enums::GsmFeature::Retry => {
                let gsm_db_record_retry_feature_data =
                    gsm_db_record.feature_data.get_retry_feature_data();

                let retry_feature_data = common_types::domain::GsmFeatureData::Retry(
                    common_types::domain::RetryFeatureData {
                        step_up_possible: gsm_update_request
                            .step_up_possible
                            .or(gsm_db_record_retry_feature_data
                                .clone()
                                .map(|data| data.step_up_possible))
                            .unwrap_or_default(),
                        clear_pan_possible: gsm_update_request
                            .clear_pan_possible
                            .or(gsm_db_record_retry_feature_data
                                .clone()
                                .map(|data| data.clear_pan_possible))
                            .unwrap_or_default(),
                        alternate_network_possible: gsm_db_record_retry_feature_data
                            .map(|data| data.alternate_network_possible)
                            .unwrap_or_default(),
                        decision: gsm_update_request
                            .decision
                            .or(Some(gsm_db_record.feature_data.get_decision()))
                            .unwrap_or_default(),
                    },
                );
                (api_enums::GsmFeature::Retry, retry_feature_data)
            }
        }
    }
}

impl ForeignFrom<hyperswitch_domain_models::gsm::GatewayStatusMap> for gsm_api_types::GsmResponse {
    fn foreign_from(value: hyperswitch_domain_models::gsm::GatewayStatusMap) -> Self {
        Self {
            connector: value.connector.to_string(),
            flow: value.flow,
            sub_flow: value.sub_flow,
            code: value.code,
            message: value.message,
            decision: value.feature_data.get_decision(),
            status: value.status,
            router_error: value.router_error,
            step_up_possible: value
                .feature_data
                .get_retry_feature_data()
                .map(|data| data.is_step_up_possible())
                .unwrap_or(false),
            unified_code: value.unified_code,
            unified_message: value.unified_message,
            error_category: value.error_category,
            clear_pan_possible: value
                .feature_data
                .get_retry_feature_data()
                .map(|data| data.is_clear_pan_possible())
                .unwrap_or(false),
            feature_data: Some(value.feature_data),
            feature: value.feature,
        }
    }
}

#[cfg(feature = "v2")]
impl ForeignFrom<&domain::Customer> for payments::CustomerDetailsResponse {
    fn foreign_from(_customer: &domain::Customer) -> Self {
        todo!()
    }
}

#[cfg(feature = "v1")]
impl ForeignFrom<&domain::Customer> for payments::CustomerDetailsResponse {
    fn foreign_from(customer: &domain::Customer) -> Self {
        Self {
            id: Some(customer.customer_id.clone()),
            name: customer
                .name
                .as_ref()
                .map(|name| name.get_inner().to_owned()),
            email: customer.email.clone().map(Into::into),
            phone: customer
                .phone
                .as_ref()
                .map(|phone| phone.get_inner().to_owned()),
            phone_country_code: customer.phone_country_code.clone(),
        }
    }
}

#[cfg(feature = "olap")]
impl ForeignTryFrom<api_types::webhook_events::EventListConstraints>
    for api_types::webhook_events::EventListConstraintsInternal
{
    type Error = error_stack::Report<errors::ApiErrorResponse>;

    fn foreign_try_from(
        item: api_types::webhook_events::EventListConstraints,
    ) -> Result<Self, Self::Error> {
        if item.object_id.is_some()
            && (item.created_after.is_some()
                || item.created_before.is_some()
                || item.limit.is_some()
                || item.offset.is_some()
                || item.event_classes.is_some()
                || item.event_types.is_some())
        {
            return Err(report!(errors::ApiErrorResponse::PreconditionFailed {
                message:
                    "Either only `object_id` must be specified, or one or more of \
                          `created_after`, `created_before`, `limit`, `offset`, `event_classes` and `event_types` must be specified"
                        .to_string()
            }));
        }

        match item.object_id {
            Some(object_id) => Ok(Self::ObjectIdFilter { object_id }),
            None => Ok(Self::GenericFilter {
                created_after: item.created_after,
                created_before: item.created_before,
                limit: item.limit.map(i64::from),
                offset: item.offset.map(i64::from),
                event_classes: item.event_classes,
                event_types: item.event_types,
                is_delivered: item.is_delivered,
            }),
        }
    }
}

#[cfg(feature = "olap")]
impl TryFrom<domain::Event> for api_models::webhook_events::EventListItemResponse {
    type Error = error_stack::Report<errors::ApiErrorResponse>;

    fn try_from(item: domain::Event) -> Result<Self, Self::Error> {
        use crate::utils::OptionExt;

        // We only allow retrieving events with merchant_id, business_profile_id
        // and initial_attempt_id populated.
        // We cannot retrieve events with only some of these fields populated.
        let merchant_id = item
            .merchant_id
            .get_required_value("merchant_id")
            .change_context(errors::ApiErrorResponse::InternalServerError)?;
        let profile_id = item
            .business_profile_id
            .get_required_value("business_profile_id")
            .change_context(errors::ApiErrorResponse::InternalServerError)?;
        let initial_attempt_id = item
            .initial_attempt_id
            .get_required_value("initial_attempt_id")
            .change_context(errors::ApiErrorResponse::InternalServerError)?;

        Ok(Self {
            event_id: item.event_id,
            merchant_id,
            profile_id,
            object_id: item.primary_object_id,
            event_type: item.event_type,
            event_class: item.event_class,
            is_delivery_successful: item.is_overall_delivery_successful,
            initial_attempt_id,
            created: item.created_at,
        })
    }
}

#[cfg(feature = "olap")]
impl TryFrom<domain::Event> for api_models::webhook_events::EventRetrieveResponse {
    type Error = error_stack::Report<errors::ApiErrorResponse>;

    fn try_from(item: domain::Event) -> Result<Self, Self::Error> {
        use crate::utils::OptionExt;

        // We only allow retrieving events with all required fields in `EventListItemResponse`, and
        // `request` and `response` populated.
        // We cannot retrieve events with only some of these fields populated.
        let event_information =


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

impl api::RefundExecute for for Paypal


// Struct: ExtendedAuthorizationResponseData
// File: crates/hyperswitch_domain_models/src/router_data.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct ExtendedAuthorizationResponseData


// Struct: FluidData
// File: crates/hyperswitch_connectors/src/connectors/barclaycard/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FluidData


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

pub struct SantanderRouterData<T>