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

use common_utils::id_type;

use super::MockDb;
use crate::{
    core::errors::{self, CustomResult},
    types::storage::{self as storage_types, enums::MerchantStorageScheme},
};

#[async_trait::async_trait]
pub trait MandateInterface {
    async fn find_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError>;

    async fn find_mandate_by_merchant_id_connector_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_mandate_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError>;

    async fn find_mandate_by_merchant_id_customer_id(
        &self,
        merchant_id: &id_type::MerchantId,
        customer_id: &id_type::CustomerId,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError>;

    // Fix this function once we move to mandate v2
    #[cfg(feature = "v2")]
    async fn find_mandate_by_global_customer_id(
        &self,
        id: &id_type::GlobalCustomerId,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError>;

    async fn update_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        mandate_update: storage_types::MandateUpdate,
        mandate: storage_types::Mandate,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError>;

    async fn find_mandates_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_constraints: api_models::mandates::MandateListConstraints,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError>;

    async fn insert_mandate(
        &self,
        mandate: storage_types::MandateNew,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError>;
}

#[cfg(feature = "kv_store")]
mod storage {
    use common_utils::{fallback_reverse_lookup_not_found, id_type};
    use diesel_models::kv;
    use error_stack::{report, ResultExt};
    use redis_interface::HsetnxReply;
    use router_env::{instrument, tracing};
    use storage_impl::redis::kv_store::{
        decide_storage_scheme, kv_wrapper, KvOperation, Op, PartitionKey,
    };

    use super::MandateInterface;
    use crate::{
        connection,
        core::errors::{self, utils::RedisErrorExt, CustomResult},
        db::reverse_lookup::ReverseLookupInterface,
        services::Store,
        types::storage::{self as storage_types, enums::MerchantStorageScheme, MandateDbExt},
        utils::db_utils,
    };

    #[async_trait::async_trait]
    impl MandateInterface for Store {
        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_id: &str,
            storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            let database_call = || async {
                storage_types::Mandate::find_by_merchant_id_mandate_id(
                    &conn,
                    merchant_id,
                    mandate_id,
                )
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
            };
            let storage_scheme = Box::pin(decide_storage_scheme::<_, diesel_models::Mandate>(
                self,
                storage_scheme,
                Op::Find,
            ))
            .await;
            match storage_scheme {
                MerchantStorageScheme::PostgresOnly => database_call().await,
                MerchantStorageScheme::RedisKv => {
                    let key = PartitionKey::MerchantIdMandateId {
                        merchant_id,
                        mandate_id,
                    };
                    let field = format!("mandate_{mandate_id}");

                    Box::pin(db_utils::try_redis_get_else_try_database_get(
                        async {
                            Box::pin(kv_wrapper(
                                self,
                                KvOperation::<diesel_models::Mandate>::HGet(&field),
                                key,
                            ))
                            .await?
                            .try_into_hget()
                        },
                        database_call,
                    ))
                    .await
                }
            }
        }

        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_connector_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            connector_mandate_id: &str,
            storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            let database_call = || async {
                storage_types::Mandate::find_by_merchant_id_connector_mandate_id(
                    &conn,
                    merchant_id,
                    connector_mandate_id,
                )
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
            };
            let storage_scheme = Box::pin(decide_storage_scheme::<_, diesel_models::Mandate>(
                self,
                storage_scheme,
                Op::Find,
            ))
            .await;
            match storage_scheme {
                MerchantStorageScheme::PostgresOnly => database_call().await,
                MerchantStorageScheme::RedisKv => {
                    let lookup_id = format!(
                        "mid_{}_conn_mandate_{}",
                        merchant_id.get_string_repr(),
                        connector_mandate_id
                    );
                    let lookup = fallback_reverse_lookup_not_found!(
                        self.get_lookup_by_lookup_id(&lookup_id, storage_scheme)
                            .await,
                        database_call().await
                    );

                    let key = PartitionKey::CombinationKey {
                        combination: &lookup.pk_id,
                    };

                    Box::pin(db_utils::try_redis_get_else_try_database_get(
                        async {
                            Box::pin(kv_wrapper(
                                self,
                                KvOperation::<diesel_models::Mandate>::HGet(&lookup.sk_id),
                                key,
                            ))
                            .await?
                            .try_into_hget()
                        },
                        database_call,
                    ))
                    .await
                }
            }
        }

        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_customer_id(
            &self,
            merchant_id: &id_type::MerchantId,
            customer_id: &id_type::CustomerId,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_merchant_id_customer_id(&conn, merchant_id, customer_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[cfg(feature = "v2")]
        #[instrument(skip_all)]
        async fn find_mandate_by_global_customer_id(
            &self,
            id: &id_type::GlobalCustomerId,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_global_customer_id(&conn, id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn update_mandate_by_merchant_id_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_id: &str,
            mandate_update: storage_types::MandateUpdate,
            mandate: storage_types::Mandate,
            storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_write(self).await?;
            let key = PartitionKey::MerchantIdMandateId {
                merchant_id,
                mandate_id,
            };
            let field = format!("mandate_{mandate_id}");
            let storage_scheme = Box::pin(decide_storage_scheme::<_, diesel_models::Mandate>(
                self,
                storage_scheme,
                Op::Update(key.clone(), &field, mandate.updated_by.as_deref()),
            ))
            .await;
            match storage_scheme {
                MerchantStorageScheme::PostgresOnly => {
                    storage_types::Mandate::update_by_merchant_id_mandate_id(
                        &conn,
                        merchant_id,
                        mandate_id,
                        mandate_update.convert_to_mandate_update(storage_scheme),
                    )
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error)))
                }
                MerchantStorageScheme::RedisKv => {
                    let key_str = key.to_string();

                    if let diesel_models::MandateUpdate::ConnectorMandateIdUpdate {
                        connector_mandate_id: Some(val),
                        ..
                    } = &mandate_update
                    {
                        let rev_lookup = diesel_models::ReverseLookupNew {
                            sk_id: field.clone(),
                            pk_id: key_str.clone(),
                            lookup_id: format!(
                                "mid_{}_conn_mandate_{}",
                                merchant_id.get_string_repr(),
                                val
                            ),
                            source: "mandate".to_string(),
                            updated_by: storage_scheme.to_string(),
                        };
                        self.insert_reverse_lookup(rev_lookup, storage_scheme)
                            .await?;
                    }

                    let m_update = mandate_update.convert_to_mandate_update(storage_scheme);
                    let updated_mandate = m_update.clone().apply_changeset(mandate.clone());

                    let redis_value = serde_json::to_string(&updated_mandate)
                        .change_context(errors::StorageError::SerializationFailed)?;

                    let redis_entry = kv::TypedSql {
                        op: kv::DBOperation::Update {
                            updatable: Box::new(kv::Updateable::MandateUpdate(
                                kv::MandateUpdateMems {
                                    orig: mandate,
                                    update_data: m_update,
                                },
                            )),
                        },
                    };

                    Box::pin(kv_wrapper::<(), _, _>(
                        self,
                        KvOperation::<diesel_models::Mandate>::Hset(
                            (&field, redis_value),
                            redis_entry,
                        ),
                        key,
                    ))
                    .await
                    .map_err(|err| err.to_redis_failed_response(&key_str))?
                    .try_into_hset()
                    .change_context(errors::StorageError::KVError)?;

                    Ok(updated_mandate)
                }
            }
        }

        #[instrument(skip_all)]
        async fn find_mandates_by_merchant_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_constraints: api_models::mandates::MandateListConstraints,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::filter_by_constraints(&conn, merchant_id, mandate_constraints)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn insert_mandate(
            &self,
            mut mandate: storage_types::MandateNew,
            storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_write(self).await?;
            let storage_scheme = Box::pin(decide_storage_scheme::<_, diesel_models::Mandate>(
                self,
                storage_scheme,
                Op::Insert,
            ))
            .await;
            mandate.update_storage_scheme(storage_scheme);
            match storage_scheme {
                MerchantStorageScheme::PostgresOnly => mandate
                    .insert(&conn)
                    .await
                    .map_err(|error| report!(errors::StorageError::from(error))),
                MerchantStorageScheme::RedisKv => {
                    let mandate_id = mandate.mandate_id.clone();
                    let merchant_id = &mandate.merchant_id.to_owned();
                    let connector_mandate_id = mandate.connector_mandate_id.clone();

                    let key = PartitionKey::MerchantIdMandateId {
                        merchant_id,
                        mandate_id: mandate_id.as_str(),
                    };
                    let key_str = key.to_string();
                    let field = format!("mandate_{mandate_id}");

                    let storage_mandate = storage_types::Mandate::from(&mandate);

                    let redis_entry = kv::TypedSql {
                        op: kv::DBOperation::Insert {
                            insertable: Box::new(kv::Insertable::Mandate(mandate)),
                        },
                    };

                    if let Some(connector_val) = connector_mandate_id {
                        let lookup_id = format!(
                            "mid_{}_conn_mandate_{}",
                            merchant_id.get_string_repr(),
                            connector_val
                        );

                        let reverse_lookup_entry = diesel_models::ReverseLookupNew {
                            sk_id: field.clone(),
                            pk_id: key_str.clone(),
                            lookup_id,
                            source: "mandate".to_string(),
                            updated_by: storage_scheme.to_string(),
                        };

                        self.insert_reverse_lookup(reverse_lookup_entry, storage_scheme)
                            .await?;
                    }

                    match Box::pin(kv_wrapper::<diesel_models::Mandate, _, _>(
                        self,
                        KvOperation::<diesel_models::Mandate>::HSetNx(
                            &field,
                            &storage_mandate,
                            redis_entry,
                        ),
                        key,
                    ))
                    .await
                    .map_err(|err| err.to_redis_failed_response(&key_str))?
                    .try_into_hsetnx()
                    {
                        Ok(HsetnxReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue {
                            entity: "mandate",
                            key: Some(storage_mandate.mandate_id),
                        }
                        .into()),
                        Ok(HsetnxReply::KeySet) => Ok(storage_mandate),
                        Err(er) => Err(er).change_context(errors::StorageError::KVError),
                    }
                }
            }
        }
    }
}

#[cfg(not(feature = "kv_store"))]
mod storage {
    use common_utils::id_type;
    use error_stack::report;
    use router_env::{instrument, tracing};

    use super::MandateInterface;
    use crate::{
        connection,
        core::errors::{self, CustomResult},
        services::Store,
        types::storage::{self as storage_types, enums::MerchantStorageScheme, MandateDbExt},
    };

    #[async_trait::async_trait]
    impl MandateInterface for Store {
        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_id: &str,
            _storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_merchant_id_mandate_id(&conn, merchant_id, mandate_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_connector_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            connector_mandate_id: &str,
            _storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_merchant_id_connector_mandate_id(
                &conn,
                merchant_id,
                connector_mandate_id,
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn find_mandate_by_merchant_id_customer_id(
            &self,
            merchant_id: &id_type::MerchantId,
            customer_id: &id_type::CustomerId,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_merchant_id_customer_id(&conn, merchant_id, customer_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        // Need to fix this once we start moving to mandate v2
        #[cfg(feature = "v2")]
        #[instrument(skip_all)]
        async fn find_mandate_by_global_customer_id(
            &self,
            customer_id: &id_type::GlobalCustomerId,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::find_by_global_id(&conn, customer_id)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn update_mandate_by_merchant_id_mandate_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_id: &str,
            mandate_update: storage_types::MandateUpdate,
            _mandate: storage_types::Mandate,
            _storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_write(self).await?;
            storage_types::Mandate::update_by_merchant_id_mandate_id(
                &conn,
                merchant_id,
                mandate_id,
                storage_types::MandateUpdateInternal::from(mandate_update),
            )
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn find_mandates_by_merchant_id(
            &self,
            merchant_id: &id_type::MerchantId,
            mandate_constraints: api_models::mandates::MandateListConstraints,
        ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
            let conn = connection::pg_connection_read(self).await?;
            storage_types::Mandate::filter_by_constraints(&conn, merchant_id, mandate_constraints)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }

        #[instrument(skip_all)]
        async fn insert_mandate(
            &self,
            mandate: storage_types::MandateNew,
            _storage_scheme: MerchantStorageScheme,
        ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
            let conn = connection::pg_connection_write(self).await?;
            mandate
                .insert(&conn)
                .await
                .map_err(|error| report!(errors::StorageError::from(error)))
        }
    }
}

#[async_trait::async_trait]
impl MandateInterface for MockDb {
    async fn find_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
        self.mandates
            .lock()
            .await
            .iter()
            .find(|mandate| mandate.merchant_id == *merchant_id && mandate.mandate_id == mandate_id)
            .cloned()
            .ok_or_else(|| errors::StorageError::ValueNotFound("mandate not found".to_string()))
            .map_err(|err| err.into())
    }

    async fn find_mandate_by_merchant_id_connector_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_mandate_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
        self.mandates
            .lock()
            .await
            .iter()
            .find(|mandate| {
                mandate.merchant_id == *merchant_id
                    && mandate.connector_mandate_id == Some(connector_mandate_id.to_string())
            })
            .cloned()
            .ok_or_else(|| errors::StorageError::ValueNotFound("mandate not found".to_string()))
            .map_err(|err| err.into())
    }

    async fn find_mandate_by_merchant_id_customer_id(
        &self,
        merchant_id: &id_type::MerchantId,
        customer_id: &id_type::CustomerId,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
        return Ok(self
            .mandates
            .lock()
            .await
            .iter()
            .filter(|mandate| {
                mandate.merchant_id == *merchant_id && &mandate.customer_id == customer_id
            })
            .cloned()
            .collect());
    }

    // Need to fix this once we move to v2 mandate
    #[cfg(feature = "v2")]
    async fn find_mandate_by_global_customer_id(
        &self,
        id: &id_type::GlobalCustomerId,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
        todo!()
    }

    async fn update_mandate_by_merchant_id_mandate_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_id: &str,
        mandate_update: storage_types::MandateUpdate,
        _mandate: storage_types::Mandate,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
        let mut mandates = self.mandates.lock().await;
        match mandates
            .iter_mut()
            .find(|mandate| mandate.merchant_id == *merchant_id && mandate.mandate_id == mandate_id)
        {
            Some(mandate) => {
                let m_update = diesel_models::MandateUpdateInternal::from(mandate_update);
                let updated_mandate = m_update.clone().apply_changeset(mandate.clone());
                Ok(updated_mandate)
            }
            None => {
                Err(errors::StorageError::ValueNotFound("mandate not found".to_string()).into())
            }
        }
    }

    async fn find_mandates_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
        mandate_constraints: api_models::mandates::MandateListConstraints,
    ) -> CustomResult<Vec<storage_types::Mandate>, errors::StorageError> {
        let mandates = self.mandates.lock().await;
        let mandates_iter = mandates.iter().filter(|mandate| {
            let mut checker = mandate.merchant_id == *merchant_id;
            if let Some(created_time) = mandate_constraints.created_time {
                checker &= mandate.created_at == created_time;
            }
            if let Some(created_time_lt) = mandate_constraints.created_time_lt {
                checker &= mandate.created_at < created_time_lt;
            }
            if let Some(created_time_gt) = mandate_constraints.created_time_gt {
                checker &= mandate.created_at > created_time_gt;
            }
            if let Some(created_time_lte) = mandate_constraints.created_time_lte {
                checker &= mandate.created_at <= created_time_lte;
            }
            if let Some(created_time_gte) = mandate_constraints.created_time_gte {
                checker &= mandate.created_at >= created_time_gte;
            }
            if let Some(connector) = &mandate_constraints.connector {
                checker &= mandate.connector == *connector;
            }
            if let Some(mandate_status) = mandate_constraints.mandate_status {
                checker &= mandate.mandate_status == mandate_status;
            }
            checker
        });

        #[allow(clippy::as_conversions)]
        let offset = (if mandate_constraints.offset.unwrap_or(0) < 0 {
            0
        } else {
            mandate_constraints.offset.unwrap_or(0)
        }) as usize;

        let mandates: Vec<storage_types::Mandate> = if let Some(limit) = mandate_constraints.limit {
            #[allow(clippy::as_conversions)]
            mandates_iter
                .skip(offset)
                .take((if limit < 0 { 0 } else { limit }) as usize)
                .cloned()
                .collect()
        } else {
            mandates_iter.skip(offset).cloned().collect()
        };
        Ok(mandates)
    }

    async fn insert_mandate(
        &self,
        mandate_new: storage_types::MandateNew,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage_types::Mandate, errors::StorageError> {
        let mut mandates = self.mandates.lock().await;
        let customer_user_agent_extended = mandate_new.get_customer_user_agent_extended();

        let mandate = storage_types::Mandate {
            mandate_id: mandate_new.mandate_id.clone(),
            customer_id: mandate_new.customer_id,
            merchant_id: mandate_new.merchant_id,
            original_payment_id: mandate_new.original_payment_id,
            payment_method_id: mandate_new.payment_method_id,
            mandate_status: mandate_new.mandate_status,
            mandate_type: mandate_new.mandate_type,
            customer_accepted_at: mandate_new.customer_accepted_at,
            customer_ip_address: mandate_new.customer_ip_address,
            customer_user_agent: None,
            network_transaction_id: mandate_new.network_transaction_id,
            previous_attempt_id: mandate_new.previous_attempt_id,
            created_at: mandate_new
                .created_at
                .unwrap_or_else(common_utils::date_time::now),
            mandate_amount: mandate_new.mandate_amount,
            mandate_currency: mandate_new.mandate_currency,
            amount_captured: mandate_new.amount_captured,
            connector: mandate_new.connector,
            connector_mandate_id: mandate_new.connector_mandate_id,
            start_date: mandate_new.start_date,
            end_date: mandate_new.end_date,
            metadata: mandate_new.metadata,
            connector_mandate_ids: mandate_new.connector_mandate_ids,
            merchant_connector_id: mandate_new.merchant_connector_id,
            updated_by: mandate_new.updated_by,
            customer_user_agent_extended,
        };
        mandates.push(mandate.clone());
        Ok(mandate)
    }
}


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

impl api::PaymentAuthorize for for Trustpayments


// Trait: PayoutAttemptInterface
// File: crates/hyperswitch_domain_models/src/lib.rs
// Module: hyperswitch_domain_models

pub trait PayoutAttemptInterface


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

pub fn add_entry_to_blocklist(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<api_blocklist::AddToBlocklistRequest>,
) -> HttpResponse


// Struct: HyperswitchVaultAuthType
// File: crates/hyperswitch_connectors/src/connectors/hyperswitch_vault/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct HyperswitchVaultAuthType


// Struct: CardInfoMigrateExecutor
// File: crates/router/src/core/cards_info.rs
// Module: router
// Implementations: 1

pub struct CardInfoMigrateExecutor<'a>


// Function: get_interac_account_id
// File: crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
// Module: hyperswitch_connectors

pub fn get_interac_account_id(
        &self,
        currency: Currency,
    ) -> Result<Secret<String>, errors::ConnectorError>


// Module Structure
// File: crates/router/src/services/authentication.rs
// Module: router

// Public submodules:
pub mod blacklist;
pub mod cookies;
pub mod decision;


// Function: generate_aes256_key
// File: crates/router/src/services.rs
// Module: router

pub fn generate_aes256_key() -> errors::CustomResult<[u8


// Function: insert_user_in_blacklist
// File: crates/router/src/services/authentication/blacklist.rs
// Module: router

pub fn insert_user_in_blacklist(state: &SessionState, user_id: &str) -> UserResult<()>


// Function: from_storage
// File: crates/router/src/services/kafka/payment_attempt.rs
// Module: router

pub fn from_storage(attempt: &'a PaymentAttempt) -> Self


// Function: submit_dispute_evidence
// File: crates/router/src/routes/disputes.rs
// Module: router

pub fn submit_dispute_evidence(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<dispute_models::SubmitEvidenceRequest>,
) -> HttpResponse


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

pub struct NovalnetResponseMerchant


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

pub fn delete_card_by_locker_id(
    state: &routes::SessionState,
    id: &id_type::GlobalCustomerId,
    merchant_id: &id_type::MerchantId,
) -> errors::RouterResult<payment_methods::DeleteCardResp>


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

pub fn retrieve_payment_method_with_token(
    _state: &SessionState,
    _merchant_key_store: &domain::MerchantKeyStore,
    _token_data: &storage::PaymentTokenData,
    _payment_intent: &PaymentIntent,
    _card_token_data: Option<&domain::CardToken>,
    _customer: &Option<domain::Customer>,
    _storage_scheme: common_enums::enums::MerchantStorageScheme,
    _mandate_id: Option<api_models::payments::MandateIds>,
    _payment_method_info: Option<domain::PaymentMethod>,
    _business_profile: &domain::Profile,
) -> RouterResult<storage::PaymentMethodDataWithId>


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

impl api::RefundSync for for Globepay


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

pub fn insert_surcharge_details(
        &mut self,
        surcharge_key: SurchargeKey,
        surcharge_details: SurchargeDetails,
    )


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

pub struct GocardlessBankAccountResponse


// File: crates/hyperswitch_domain_models/src/payment_methods.rs
// Module: hyperswitch_domain_models
// Public functions: 14
// Public structs: 7

#[cfg(feature = "v2")]
use api_models::payment_methods::PaymentMethodsData;
use api_models::{customers, payment_methods, payments};
// specific imports because of using the macro
use common_enums::enums::MerchantStorageScheme;
#[cfg(feature = "v1")]
use common_utils::crypto::OptionalEncryptableValue;
#[cfg(feature = "v2")]
use common_utils::{crypto::Encryptable, encryption::Encryption, types::keymanager::ToEncryptable};
use common_utils::{
    errors::{CustomResult, ParsingError, ValidationError},
    id_type, pii, type_name,
    types::keymanager,
};
pub use diesel_models::{enums as storage_enums, PaymentMethodUpdate};
use error_stack::ResultExt;
#[cfg(feature = "v1")]
use masking::ExposeInterface;
use masking::{PeekInterface, Secret};
#[cfg(feature = "v1")]
use router_env::logger;
#[cfg(feature = "v2")]
use rustc_hash::FxHashMap;
#[cfg(feature = "v2")]
use serde_json::Value;
use time::PrimitiveDateTime;

#[cfg(feature = "v2")]
use crate::address::Address;
#[cfg(feature = "v1")]
use crate::type_encryption::AsyncLift;
use crate::{
    mandates::{self, CommonMandateReference},
    merchant_key_store::MerchantKeyStore,
    payment_method_data as domain_payment_method_data,
    transformers::ForeignTryFrom,
    type_encryption::{crypto_operation, CryptoOperation},
};

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct VaultId(String);

impl VaultId {
    pub fn get_string_repr(&self) -> &String {
        &self.0
    }

    pub fn generate(id: String) -> Self {
        Self(id)
    }
}
#[cfg(feature = "v1")]
#[derive(Clone, Debug)]
pub struct PaymentMethod {
    pub customer_id: id_type::CustomerId,
    pub merchant_id: id_type::MerchantId,
    pub payment_method_id: String,
    pub accepted_currency: Option<Vec<storage_enums::Currency>>,
    pub scheme: Option<String>,
    pub token: Option<String>,
    pub cardholder_name: Option<Secret<String>>,
    pub issuer_name: Option<String>,
    pub issuer_country: Option<String>,
    pub payer_country: Option<Vec<String>>,
    pub is_stored: Option<bool>,
    pub swift_code: Option<String>,
    pub direct_debit_token: Option<String>,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub payment_method: Option<storage_enums::PaymentMethod>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub payment_method_issuer: Option<String>,
    pub payment_method_issuer_code: Option<storage_enums::PaymentMethodIssuerCode>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub payment_method_data: OptionalEncryptableValue,
    pub locker_id: Option<String>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<serde_json::Value>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    pub payment_method_billing_address: OptionalEncryptableValue,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    pub network_token_payment_method_data: OptionalEncryptableValue,
    pub vault_source_details: PaymentMethodVaultSourceDetails,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, router_derive::ToEncryption)]
pub struct PaymentMethod {
    /// The identifier for the payment method. Using this recurring payments can be made
    pub id: id_type::GlobalPaymentMethodId,

    /// The customer id against which the payment method is saved
    pub customer_id: id_type::GlobalCustomerId,

    /// The merchant id against which the payment method is saved
    pub merchant_id: id_type::MerchantId,
    /// The merchant connector account id of the external vault where the payment method is saved
    pub external_vault_source: Option<id_type::MerchantConnectorAccountId>,
    pub created_at: PrimitiveDateTime,
    pub last_modified: PrimitiveDateTime,
    pub payment_method_type: Option<storage_enums::PaymentMethod>,
    pub payment_method_subtype: Option<storage_enums::PaymentMethodType>,
    #[encrypt(ty = Value)]
    pub payment_method_data: Option<Encryptable<PaymentMethodsData>>,
    pub locker_id: Option<VaultId>,
    pub last_used_at: PrimitiveDateTime,
    pub connector_mandate_details: Option<CommonMandateReference>,
    pub customer_acceptance: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::PaymentMethodStatus,
    pub network_transaction_id: Option<String>,
    pub client_secret: Option<String>,
    #[encrypt(ty = Value)]
    pub payment_method_billing_address: Option<Encryptable<Address>>,
    pub updated_by: Option<String>,
    pub locker_fingerprint_id: Option<String>,
    pub version: common_enums::ApiVersion,
    pub network_token_requestor_reference_id: Option<String>,
    pub network_token_locker_id: Option<String>,
    #[encrypt(ty = Value)]
    pub network_token_payment_method_data:
        Option<Encryptable<domain_payment_method_data::PaymentMethodsData>>,
    #[encrypt(ty = Value)]
    pub external_vault_token_data:
        Option<Encryptable<api_models::payment_methods::ExternalVaultTokenData>>,
    pub vault_type: Option<storage_enums::VaultType>,
}

impl PaymentMethod {
    #[cfg(feature = "v1")]
    pub fn get_id(&self) -> &String {
        &self.payment_method_id
    }

    #[cfg(feature = "v1")]
    pub fn get_payment_methods_data(
        &self,
    ) -> Option<domain_payment_method_data::PaymentMethodsData> {
        self.payment_method_data
            .clone()
            .map(|value| value.into_inner().expose())
            .and_then(|value| {
                serde_json::from_value::<domain_payment_method_data::PaymentMethodsData>(value)
                    .map_err(|error| {
                        logger::warn!(
                            ?error,
                            "Failed to parse payment method data in payment method info"
                        );
                    })
                    .ok()
            })
    }

    #[cfg(feature = "v2")]
    pub fn get_id(&self) -> &id_type::GlobalPaymentMethodId {
        &self.id
    }

    #[cfg(feature = "v1")]
    pub fn get_payment_method_type(&self) -> Option<storage_enums::PaymentMethod> {
        self.payment_method
    }

    #[cfg(feature = "v2")]
    pub fn get_payment_method_type(&self) -> Option<storage_enums::PaymentMethod> {
        self.payment_method_type
    }

    #[cfg(feature = "v1")]
    pub fn get_payment_method_subtype(&self) -> Option<storage_enums::PaymentMethodType> {
        self.payment_method_type
    }

    #[cfg(feature = "v2")]
    pub fn get_payment_method_subtype(&self) -> Option<storage_enums::PaymentMethodType> {
        self.payment_method_subtype
    }

    #[cfg(feature = "v1")]
    pub fn get_common_mandate_reference(&self) -> Result<CommonMandateReference, ParsingError> {
        let payments_data = self
            .connector_mandate_details
            .clone()
            .map(|mut mandate_details| {
                mandate_details
                    .as_object_mut()
                    .map(|obj| obj.remove("payouts"));

                serde_json::from_value::<mandates::PaymentsMandateReference>(mandate_details)
                    .inspect_err(|err| {
                        router_env::logger::error!("Failed to parse payments data: {:?}", err);
                    })
            })
            .transpose()
            .map_err(|err| {
                router_env::logger::error!("Failed to parse payments data: {:?}", err);
                ParsingError::StructParseFailure("Failed to parse payments data")
            })?;

        let payouts_data = self
            .connector_mandate_details
            .clone()
            .map(|mandate_details| {
                serde_json::from_value::<Option<CommonMandateReference>>(mandate_details)
                    .inspect_err(|err| {
                        router_env::logger::error!("Failed to parse payouts data: {:?}", err);
                    })
                    .map(|optional_common_mandate_details| {
                        optional_common_mandate_details
                            .and_then(|common_mandate_details| common_mandate_details.payouts)
                    })
            })
            .transpose()
            .map_err(|err| {
                router_env::logger::error!("Failed to parse payouts data: {:?}", err);
                ParsingError::StructParseFailure("Failed to parse payouts data")
            })?
            .flatten();

        Ok(CommonMandateReference {
            payments: payments_data,
            payouts: payouts_data,
        })
    }

    #[cfg(feature = "v2")]
    pub fn get_common_mandate_reference(&self) -> Result<CommonMandateReference, ParsingError> {
        if let Some(value) = &self.connector_mandate_details {
            Ok(value.clone())
        } else {
            Ok(CommonMandateReference {
                payments: None,
                payouts: None,
            })
        }
    }

    #[cfg(feature = "v2")]
    pub fn set_payment_method_type(&mut self, payment_method_type: common_enums::PaymentMethod) {
        self.payment_method_type = Some(payment_method_type);
    }

    #[cfg(feature = "v2")]
    pub fn set_payment_method_subtype(
        &mut self,
        payment_method_subtype: common_enums::PaymentMethodType,
    ) {
        self.payment_method_subtype = Some(payment_method_subtype);
    }
}

#[cfg(feature = "v1")]
#[async_trait::async_trait]
impl super::behaviour::Conversion for PaymentMethod {
    type DstType = diesel_models::payment_method::PaymentMethod;
    type NewDstType = diesel_models::payment_method::PaymentMethodNew;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        let (vault_type, external_vault_source) = self.vault_source_details.into();
        Ok(Self::DstType {
            customer_id: self.customer_id,
            merchant_id: self.merchant_id,
            payment_method_id: self.payment_method_id,
            accepted_currency: self.accepted_currency,
            scheme: self.scheme,
            token: self.token,
            cardholder_name: self.cardholder_name,
            issuer_name: self.issuer_name,
            issuer_country: self.issuer_country,
            payer_country: self.payer_country,
            is_stored: self.is_stored,
            swift_code: self.swift_code,
            direct_debit_token: self.direct_debit_token,
            created_at: self.created_at,
            last_modified: self.last_modified,
            payment_method: self.payment_method,
            payment_method_type: self.payment_method_type,
            payment_method_issuer: self.payment_method_issuer,
            payment_method_issuer_code: self.payment_method_issuer_code,
            metadata: self.metadata,
            payment_method_data: self.payment_method_data.map(|val| val.into()),
            locker_id: self.locker_id,
            last_used_at: self.last_used_at,
            connector_mandate_details: self.connector_mandate_details,
            customer_acceptance: self.customer_acceptance,
            status: self.status,
            network_transaction_id: self.network_transaction_id,
            client_secret: self.client_secret,
            payment_method_billing_address: self
                .payment_method_billing_address
                .map(|val| val.into()),
            updated_by: self.updated_by,
            version: self.version,
            network_token_requestor_reference_id: self.network_token_requestor_reference_id,
            network_token_locker_id: self.network_token_locker_id,
            network_token_payment_method_data: self
                .network_token_payment_method_data
                .map(|val| val.into()),
            external_vault_source,
            vault_type,
        })
    }

    async fn convert_back(
        state: &keymanager::KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        // Decrypt encrypted fields first
        let (
            payment_method_data,
            payment_method_billing_address,
            network_token_payment_method_data,
        ) = async {
            let payment_method_data = item
                .payment_method_data
                .async_lift(|inner| async {
                    crypto_operation(
                        state,
                        type_name!(Self::DstType),
                        CryptoOperation::DecryptOptional(inner),
                        key_manager_identifier.clone(),
                        key.peek(),
                    )
                    .await
                    .and_then(|val| val.try_into_optionaloperation())
                })
                .await?;

            let payment_method_billing_address = item
                .payment_method_billing_address
                .async_lift(|inner| async {
                    crypto_operation(
                        state,
                        type_name!(Self::DstType),
                        CryptoOperation::DecryptOptional(inner),
                        key_manager_identifier.clone(),
                        key.peek(),
                    )
                    .await
                    .and_then(|val| val.try_into_optionaloperation())
                })
                .await?;

            let network_token_payment_method_data = item
                .network_token_payment_method_data
                .async_lift(|inner| async {
                    crypto_operation(
                        state,
                        type_name!(Self::DstType),
                        CryptoOperation::DecryptOptional(inner),
                        key_manager_identifier.clone(),
                        key.peek(),
                    )
                    .await
                    .and_then(|val| val.try_into_optionaloperation())
                })
                .await?;

            Ok::<_, error_stack::Report<common_utils::errors::CryptoError>>((
                payment_method_data,
                payment_method_billing_address,
                network_token_payment_method_data,
            ))
        }
        .await
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting payment method data".to_string(),
        })?;

        let vault_source_details = PaymentMethodVaultSourceDetails::try_from((
            item.vault_type,
            item.external_vault_source,
        ))?;

        // Construct the domain type
        Ok(Self {
            customer_id: item.customer_id,
            merchant_id: item.merchant_id,
            payment_method_id: item.payment_method_id,
            accepted_currency: item.accepted_currency,
            scheme: item.scheme,
            token: item.token,
            cardholder_name: item.cardholder_name,
            issuer_name: item.issuer_name,
            issuer_country: item.issuer_country,
            payer_country: item.payer_country,
            is_stored: item.is_stored,
            swift_code: item.swift_code,
            direct_debit_token: item.direct_debit_token,
            created_at: item.created_at,
            last_modified: item.last_modified,
            payment_method: item.payment_method,
            payment_method_type: item.payment_method_type,
            payment_method_issuer: item.payment_method_issuer,
            payment_method_issuer_code: item.payment_method_issuer_code,
            metadata: item.metadata,
            payment_method_data,
            locker_id: item.locker_id,
            last_used_at: item.last_used_at,
            connector_mandate_details: item.connector_mandate_details,
            customer_acceptance: item.customer_acceptance,
            status: item.status,
            network_transaction_id: item.network_transaction_id,
            client_secret: item.client_secret,
            payment_method_billing_address,
            updated_by: item.updated_by,
            version: item.version,
            network_token_requestor_reference_id: item.network_token_requestor_reference_id,
            network_token_locker_id: item.network_token_locker_id,
            network_token_payment_method_data,
            vault_source_details,
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        let (vault_type, external_vault_source) = self.vault_source_details.into();
        Ok(Self::NewDstType {
            customer_id: self.customer_id,
            merchant_id: self.merchant_id,
            payment_method_id: self.payment_method_id,
            accepted_currency: self.accepted_currency,
            scheme: self.scheme,
            token: self.token,
            cardholder_name: self.cardholder_name,
            issuer_name: self.issuer_name,
            issuer_country: self.issuer_country,
            payer_country: self.payer_country,
            is_stored: self.is_stored,
            swift_code: self.swift_code,
            direct_debit_token: self.direct_debit_token,
            created_at: self.created_at,
            last_modified: self.last_modified,
            payment_method: self.payment_method,
            payment_method_type: self.payment_method_type,
            payment_method_issuer: self.payment_method_issuer,
            payment_method_issuer_code: self.payment_method_issuer_code,
            metadata: self.metadata,
            payment_method_data: self.payment_method_data.map(|val| val.into()),
            locker_id: self.locker_id,
            last_used_at: self.last_used_at,
            connector_mandate_details: self.connector_mandate_details,
            customer_acceptance: self.customer_acceptance,
            status: self.status,
            network_transaction_id: self.network_transaction_id,
            client_secret: self.client_secret,
            payment_method_billing_address: self
                .payment_method_billing_address
                .map(|val| val.into()),
            updated_by: self.updated_by,
            version: self.version,
            network_token_requestor_reference_id: self.network_token_requestor_reference_id,
            network_token_locker_id: self.network_token_locker_id,
            network_token_payment_method_data: self
                .network_token_payment_method_data
                .map(|val| val.into()),
            external_vault_source,
            vault_type,
        })
    }
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl super::behaviour::Conversion for PaymentMethod {
    type DstType = diesel_models::payment_method::PaymentMethod;
    type NewDstType = diesel_models::payment_method::PaymentMethodNew;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(Self::DstType {
            customer_id: self.customer_id,
            merchant_id: self.merchant_id,
            id: self.id,
            created_at: self.created_at,
            last_modified: self.last_modified,
            payment_method_type_v2: self.payment_method_type,
            payment_method_subtype: self.payment_method_subtype,
            payment_method_data: self.payment_method_data.map(|val| val.into()),
            locker_id: self.locker_id.map(|id| id.get_string_repr().clone()),
            last_used_at: self.last_used_at,
            connector_mandate_details: self.connector_mandate_details.map(|cmd| cmd.into()),
            customer_acceptance: self.customer_acceptance,
            status: self.status,
            network_transaction_id: self.network_transaction_id,
            client_secret: self.client_secret,
            payment_method_billing_address: self
                .payment_method_billing_address
                .map(|val| val.into()),
            updated_by: self.updated_by,
            locker_fingerprint_id: self.locker_fingerprint_id,
            version: self.version,
            network_token_requestor_reference_id: self.network_token_requestor_reference_id,
            network_token_locker_id: self.network_token_locker_id,
            network_token_payment_method_data: self
                .network_token_payment_method_data
                .map(|val| val.into()),
            external_vault_source: self.external_vault_source,
            external_vault_token_data: self.external_vault_token_data.map(|val| val.into()),
            vault_type: self.vault_type,
        })
    }

    async fn convert_back(
        state: &keymanager::KeyManagerState,
        storage_model: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        use common_utils::ext_traits::ValueExt;

        async {
            let decrypted_data = crypto_operation(
                state,
                type_name!(Self::DstType),
                CryptoOperation::BatchDecrypt(EncryptedPaymentMethod::to_encryptable(
                    EncryptedPaymentMethod {
                        payment_method_data: storage_model.payment_method_data,
                        payment_method_billing_address: storage_model
                            .payment_method_billing_address,
                        network_token_payment_method_data: storage_model
                            .network_token_payment_method_data,
                        external_vault_token_data: storage_model.external_vault_token_data,
                    },
                )),
                key_manager_identifier,
                key.peek(),
            )
            .await
            .and_then(|val| val.try_into_batchoperation())?;

            let data = EncryptedPaymentMethod::from_encryptable(decrypted_data)
                .change_context(common_utils::errors::CryptoError::DecodingFailed)
                .attach_printable("Invalid batch operation data")?;

            let payment_method_billing_address = data
                .payment_method_billing_address
                .map(|billing| {
                    billing.deserialize_inner_value(|value| value.parse_value("Address"))
                })
                .transpose()
                .change_context(common_utils::errors::CryptoError::DecodingFailed)
                .attach_printable("Error while deserializing Address")?;

            let payment_method_data = data
                .payment_method_data
                .map(|payment_method_data| {
                    payment_method_data
                        .deserialize_inner_value(|value| value.parse_value("Payment Method Data"))
                })
                .transpose()
                .change_context(common_utils::errors::CryptoError::DecodingFailed)
                .attach_printable("Error while deserializing Payment Method Data")?;

            let network_token_payment_method_data = data
                .network_token_payment_method_data
                .map(|network_token_payment_method_data| {
                    network_token_payment_method_data.deserialize_inner_value(|value| {
                        value.parse_value("Network token Payment Method Data")
                    })
                })
                .transpose()
                .change_context(common_utils::errors::CryptoError::DecodingFailed)
                .attach_printable("Error while deserializing Network token Payment Method Data")?;

            let external_vault_token_data = data
                .external_vault_token_data
                .map(|external_vault_token_data| {
                    external_vault_token_data.deserialize_inner_value(|value| {
                        value.parse_value("External Vault Token Data")
                    })
                })
                .transpose()
                .change_context(common_utils::errors::CryptoError::DecodingFailed)
                .attach_printable("Error while deserializing External Vault Token Data")?;

            Ok::<Self, error_stack::Report<common_utils::errors::CryptoError>>(Self {
                customer_id: storage_model.customer_id,
                merchant_id: storage_model.merchant_id,
                id: storage_model.id,
                created_at: storage_model.created_at,
                last_modified: storage_model.last_modified,
                payment_method_type: storage_model.payment_method_type_v2,
                payment_method_subtype: storage_model.payment_method_subtype,
                payment_method_data,
                locker_id: storage_model.locker_id.map(VaultId::generate),
                last_used_at: storage_model.last_used_at,
                connector_mandate_details: storage_model.connector_mandate_details.map(From::from),
                customer_acceptance: storage_model.customer_acceptance,
                status: storage_model.status,
                network_transaction_id: storage_model.network_transaction_id,
                client_secret: storage_model.client_secret,
                payment_method_billing_address,
                updated_by: storage_model.updated_by,
                locker_fingerprint_id: storage_model.locker_fingerprint_id,
                version: storage_model.version,
                network_token_requestor_reference_id: storage_model
                    .network_token_requestor_reference_id,
                network_token_locker_id: storage_model.network_token_locker_id,
                network_token_payment_method_data,
                external_vault_source: storage_model.external_vault_source,
                external_vault_token_data,
                vault_type: storage_model.vault_type,
            })
        }
        .await
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting payment method data".to_string(),
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        Ok(Self::NewDstType {
            customer_id: self.customer_id,
            merchant_id: self.merchant_id,
            id: self.id,
            created_at: self.created_at,
            last_modified: self.last_modified,
            payment_method_type_v2: self.payment_method_type,
            payment_method_subtype: self.payment_method_subtype,
            payment_method_data: self.payment_method_data.map(|val| val.into()),
            locker_id: self.locker_id.map(|id| id.get_string_repr().clone()),
            last_used_at: self.last_used_at,
            connector_mandate_details: self.connector_mandate_details.map(|cmd| cmd.into()),
            customer_acceptance: self.customer_acceptance,
            status: self.status,
            network_transaction_id: self.network_transaction_id,
            client_secret: self.client_secret,
            payment_method_billing_address: self
                .payment_method_billing_address
                .map(|val| val.into()),
            updated_by: self.updated_by,
            locker_fingerprint_id: self.locker_fingerprint_id,
            version: self.version,
            network_token_requestor_reference_id: self.network_token_requestor_reference_id,
            network_token_locker_id: self.network_token_locker_id,
            network_token_payment_method_data: self
                .network_token_payment_method_data
                .map(|val| val.into()),
            external_vault_token_data: self.external_vault_token_data.map(|val| val.into()),
            vault_type: self.vault_type,
        })
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, router_derive::ToEncryption)]
pub struct PaymentMethodSession {
    pub id: id_type::GlobalPaymentMethodSessionId,
    pub customer_id: id_type::GlobalCustomerId,
    #[encrypt(ty = Value)]
    pub billing: Option<Encryptable<Address>>,
    pub return_url: Option<common_utils::types::Url>,
    pub psp_tokenization: Option<common_types::payment_methods::PspTokenization>,
    pub network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,
    pub tokenization_data: Option<pii::SecretSerdeValue>,
    pub expires_at: PrimitiveDateTime,
    pub associated_payment_methods: Option<Vec<String>>,
    pub associated_payment: Option<id_type::GlobalPaymentId>,
    pub associated_token_id: Option<id_type::GlobalTokenId>,
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl super::behaviour::Conversion for PaymentMethodSession {
    type DstType = diesel_models::payment_methods_session::PaymentMethodSession;
    type NewDstType = diesel_models::payment_methods_session::PaymentMethodSession;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(Self::DstType {
            id: self.id,
            customer_id: self.customer_id,
            billing: self.billing.map(|val| val.into()),
            psp_tokenization: self.psp_tokenization,
            network_tokenization: self.network_tokenization,
            tokenization_data: self.tokenization_data,
            expires_at: self.expires_at,
            associated_payment_methods: self.associated_payment_methods,
            associated_payment: self.associated_payment,
            return_url: self.return_url,
            associated_token_id: self.associated_token_id,
        })
    }

    async fn convert_back(
        state: &keymanager::KeyManagerState,
        storage_model: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        use common_utils::ext_traits::ValueExt;

        async {
            let decrypted_data = crypto_operation(
                state,
                type_name!(Self::DstType),
                CryptoOperation::BatchDecrypt(EncryptedPaymentMethodSession::to_encryptable(
                    EncryptedPaymentMethodSession {
                        billing: storage_model.billing,
                    },
                )),
                key_manager_identifier,
                key.peek(),
            )
            .await
            .and_then(|val| val.try_into_batchoperation())?;

            let data = EncryptedPaymentMethodSession::from_encryptable(decrypted_data)
                .change_context(common_utils::errors::CryptoError::DecodingFailed)
                .attach_printable("Invalid batch operation data")?;

            let billing = data
                .billing
                .map(|billing| {
                    billing.deserialize_inner_value(|value| value.parse_value("Address"))
                })
                .transpose()
                .change_context(common_utils::errors::CryptoError::DecodingFailed)
                .attach_printable("Error while deserializing Address")?;

            Ok::<Self, error_stack::Report<common_utils::errors::CryptoError>>(Self {
                id: storage_model.id,
                customer_id: storage_model.customer_id,
                billing,
                psp_tokenization: storage_model.psp_tokenization,
                network_tokenization: storage_model.network_tokenization,
                tokenization_data: storage_model.tokenization_data,
                expires_at: storage_model.expires_at,
                associated_payment_methods: storage_model.associated_payment_methods,
                associated_payment: storage_model.associated_payment,
                return_url: storage_model.return_url,
                associated_token_id: storage_model.associated_token_id,
            })
        }
        .await
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting payment method data".to_string(),
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        Ok(Self::NewDstType {
            id: self.id,
            customer_id: self.customer_id,
            billing: self.billing.map(|val| val.into()),
            psp_tokenization: self.psp_tokenization,
            network_tokenization: self.network_tokenization,
            tokenization_data: self.tokenization_data,
            expires_at: self.expires_at,
            associated_payment_methods: self.associated_payment_methods,
            associated_payment: self.associated_payment,
            return_url: self.return_url,
            associated_token_id: self.associated_token_id,
        })
    }
}

#[async_trait::async_trait]
pub trait PaymentMethodInterface {
    type Error;
    #[cfg(feature = "v1")]
    async fn find_payment_method(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        payment_method_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentMethod, Self::Error>;

    #[cfg(feature = "v2")]
    async fn find_payment_method(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        payment_method_id: &id_type::GlobalPaymentMethodId,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentMethod, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_method_by_locker_id(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        locker_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentMethod, Self::Error>;

    #[cfg(feature = "v1")]
    async fn find_payment_method_by_customer_id_merchant_id_list(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
        limit: Option<i64>,
    ) -> CustomResult<Vec<PaymentMethod>, Self::Error>;

    // Need to fix this once we start moving to v2 for payment method
    #[cfg(feature = "v2")]
    async fn find_payment_method_list_by_global_customer_id(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        id: &id_type::GlobalCustomerId,
        limit: Option<i64>,
    ) -> CustomResult<Vec<PaymentMethod>, Self::Error>;

    #[cfg(feature = "v1")]
    #[allow(clippy::too_many_arguments)]
    async fn find_payment_method_by_customer_id_merchant_id_status(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
        status: common_enums::PaymentMethodStatus,
        limit: Option<i64>,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<PaymentMethod>, Self::Error>;

    #[cfg(feature = "v2")]
    #[allow(clippy::too_many_arguments)]
    async fn find_payment_method_by_global_customer_id_merchant_id_status(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        customer_id: &id_type::GlobalCustomerId,
        merchant_id: &id_type::MerchantId,
        status: common_enums::PaymentMethodStatus,
        limit: Option<i64>,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<PaymentMethod>, Self::Error>;

    #[cfg(feature = "v1")]
    async fn get_payment_method_count_by_customer_id_merchant_id_status(
        &self,
        customer_id: &id_type::CustomerId,
        merchant_id: &id_type::MerchantId,
        status: common_enums::PaymentMethodStatus,
    ) -> CustomResult<i64, Self::Error>;

    async fn get_payment_method_count_by_merchant_id_status(
        &self,
        merchant_id: &id_type::MerchantId,
        status: common_enums::PaymentMethodStatus,
    ) -> CustomResult<i64, Self::Error>;

    async fn insert_payment_method(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        payment_method: PaymentMethod,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentMethod, Self::Error>;

    async fn update_payment_method(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        payment_method: PaymentMethod,
        payment_method_update: PaymentMethodUpdate,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentMethod, Self::Error>;

    #[cfg(feature = "v2")]
    async fn delete_payment_method(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        payment_method: PaymentMethod,
    ) -> CustomResult<PaymentMethod, Self::Error>;

    #[cfg(feature = "v2")]
    async fn find_payment_method_by_fingerprint_id(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        fingerprint_id: &str,
    ) -> CustomResult<PaymentMethod, Self::Error>;

    #[cfg(feature = "v1")]
    async fn delete_payment_method_by_merchant_id_payment_method_id(
        &self,
        state: &keymanager::KeyManagerState,
        key_store: &MerchantKeyStore,
        merchant_id: &id_type::MerchantId,
        payment_method_id: &str,
    ) -> CustomResult<PaymentMethod, Self::Error>;
}

#[cfg(feature = "v2")]
pub enum PaymentMethodsSessionUpdateEnum {
    GeneralUpdate {
        billing: Box<Option<Encryptable<Address>>>,
        psp_tokenization: Option<common_types::payment_methods::PspTokenization>,
        network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,
        tokenization_data: Option<pii::SecretSerdeValue>,
    },
    UpdateAssociatedPaymentMethods {
        associated_payment_methods: Option<Vec<String>>,
    },
}

#[cfg(feature = "v2")]
impl From<PaymentMethodsSessionUpdateEnum> for PaymentMethodsSessionUpdateInternal {
    fn from(update: PaymentMethodsSessionUpdateEnum) -> Self {
        match update {
            PaymentMethodsSessionUpdateEnum::GeneralUpdate {
                billing,
                psp_tokenization,
                network_tokenization,
                tokenization_data,
            } => Self {
                billing: *billing,
                psp_tokenization,
                network_tokenization,
                tokenization_data,
                associated_payment_methods: None,
            },
            PaymentMethodsSessionUpdateEnum::UpdateAssociatedPaymentMethods {
                associated_payment_methods,
            } => Self {
                billing: None,
                psp_tokenization: None,
                network_tokenization: None,
                tokenization_data: None,
                associated_payment_methods,
            },
        }
    }
}

#[cfg(feature = "v2")]
impl PaymentMethodSession {
    pub fn apply_changeset(self, update_session: PaymentMethodsSessionUpdateInternal) -> Self {
        let Self {
            id,
            customer_id,
            billing,
            psp_tokenization,
            network_tokenization,
            tokenization_data,
            expires_at,
            return_url,
            associated_payment_methods,
            associated_payment,
            associated_token_id,
        } = self;
        Self {
            id,
            customer_id,
            billing: update_session.billing.or(billing),
            psp_tokenization: update_session.psp_tokenization.or(psp_tokenization),
            network_tokenization: update_session.network_tokenization.or(network_tokenization),
            tokenization_data: update_session.tokenization_data.or(tokenization_data),
            expires_at,
            return_url,
            associated_payment_methods: update_session
                .associated_payment_methods
                .or(associated_payment_methods),
            associated_payment,
            associated_token_id,
        }
    }
}

#[cfg(feature = "v2")]
pub struct PaymentMethodsSessionUpdateInternal {
    pub billing: Option<Encryptable<Address>>,
    pub psp_tokenization: Option<common_types::payment_methods::PspTokenization>,
    pub network_tokenization: Option<common_types::payment_methods::NetworkTokenization>,
    pub tokenization_data: Option<pii::SecretSerdeValue>,
    pub associated_payment_methods: Option<Vec<String>>,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct ConnectorCustomerDetails {
    pub connector_customer_id: String,


// File: crates/analytics/src/outgoing_webhook_event/events.rs
// Module: analytics
// Public functions: 1
// Public structs: 1

use api_models::analytics::{outgoing_webhook_event::OutgoingWebhookLogsRequest, Granularity};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use crate::{
    query::{Aggregate, GroupByClause, QueryBuilder, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, FiltersError, FiltersResult, LoadRow},
};
pub trait OutgoingWebhookLogsFilterAnalytics: LoadRow<OutgoingWebhookLogsResult> {}

pub async fn get_outgoing_webhook_event<T>(
    merchant_id: &common_utils::id_type::MerchantId,
    query_param: OutgoingWebhookLogsRequest,
    pool: &T,
) -> FiltersResult<Vec<OutgoingWebhookLogsResult>>
where
    T: AnalyticsDataSource + OutgoingWebhookLogsFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    let mut query_builder: QueryBuilder<T> =
        QueryBuilder::new(AnalyticsCollection::OutgoingWebhookEvent);
    query_builder.add_select_column("*").switch()?;

    query_builder
        .add_filter_clause("merchant_id", merchant_id)
        .switch()?;

    query_builder
        .add_filter_clause("payment_id", &query_param.payment_id)
        .switch()?;

    if let Some(event_id) = query_param.event_id {
        query_builder
            .add_filter_clause("event_id", &event_id)
            .switch()?;
    }
    if let Some(refund_id) = query_param.refund_id {
        query_builder
            .add_filter_clause("refund_id", &refund_id)
            .switch()?;
    }
    if let Some(dispute_id) = query_param.dispute_id {
        query_builder
            .add_filter_clause("dispute_id", &dispute_id)
            .switch()?;
    }
    if let Some(mandate_id) = query_param.mandate_id {
        query_builder
            .add_filter_clause("mandate_id", &mandate_id)
            .switch()?;
    }
    if let Some(payment_method_id) = query_param.payment_method_id {
        query_builder
            .add_filter_clause("payment_method_id", &payment_method_id)
            .switch()?;
    }
    if let Some(attempt_id) = query_param.attempt_id {
        query_builder
            .add_filter_clause("attempt_id", &attempt_id)
            .switch()?;
    }
    //TODO!: update the execute_query function to return reports instead of plain errors...
    query_builder
        .execute_query::<OutgoingWebhookLogsResult, _>(pool)
        .await
        .change_context(FiltersError::QueryBuildingError)?
        .change_context(FiltersError::QueryExecutionFailure)
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct OutgoingWebhookLogsResult {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub event_id: String,
    pub event_type: String,
    pub outgoing_webhook_event_type: String,
    pub payment_id: common_utils::id_type::PaymentId,
    pub refund_id: Option<String>,
    pub attempt_id: Option<String>,
    pub dispute_id: Option<String>,
    pub payment_method_id: Option<String>,
    pub mandate_id: Option<String>,
    pub content: Option<String>,
    pub is_error: bool,
    pub error: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
}


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

impl api::RefundExecute for for Opennode