File: crates/router/src/core/payouts/validator.rs
 Public functions: 5

use std::collections::HashSet;

use actix_web::http::header;
#[cfg(feature = "olap")]
use common_utils::errors::CustomResult;
use common_utils::{
    id_type::{self, GenerateId},
    validation::validate_domain_against_allowed_domains,
};
use diesel_models::generic_link::PayoutLink;
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::payment_methods::PaymentMethod;
use router_env::{instrument, tracing, which as router_env_which, Env};
use url::Url;

use super::helpers;
#[cfg(feature = "v1")]
use crate::core::payment_methods::cards::get_pm_list_context;
use crate::{
    core::{
        errors::{self, RouterResult},
        utils as core_utils,
    },
    db::StorageInterface,
    errors::StorageError,
    routes::SessionState,
    types::{api::payouts, domain, storage},
    utils,
    utils::OptionExt,
};

#[instrument(skip(db))]
pub async fn validate_uniqueness_of_payout_id_against_merchant_id(
    db: &dyn StorageInterface,
    payout_id: &id_type::PayoutId,
    merchant_id: &id_type::MerchantId,
    storage_scheme: storage::enums::MerchantStorageScheme,
) -> RouterResult<Option<storage::Payouts>> {
    let maybe_payouts = db
        .find_optional_payout_by_merchant_id_payout_id(merchant_id, payout_id, storage_scheme)
        .await;
    match maybe_payouts {
        Err(err) => {
            let storage_err = err.current_context();
            match storage_err {
                StorageError::ValueNotFound(_) => Ok(None),
                _ => Err(err
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while finding payout_attempt, database error")),
            }
        }
        Ok(payout) => Ok(payout),
    }
}

#[cfg(feature = "v2")]
pub async fn validate_create_request(
    _state: &SessionState,
    _merchant_context: &domain::MerchantContext,
    _req: &payouts::PayoutCreateRequest,
) -> RouterResult<(
    String,
    Option<payouts::PayoutMethodData>,
    String,
    Option<domain::Customer>,
    Option<PaymentMethod>,
)> {
    todo!()
}

/// Validates the request on below checks
/// - merchant_id passed is same as the one in merchant_account table
/// - payout_id is unique against merchant_id
/// - payout_token provided is legitimate
#[cfg(feature = "v1")]
pub async fn validate_create_request(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    req: &payouts::PayoutCreateRequest,
) -> RouterResult<(
    id_type::PayoutId,
    Option<payouts::PayoutMethodData>,
    id_type::ProfileId,
    Option<domain::Customer>,
    Option<PaymentMethod>,
)> {
    let merchant_id = merchant_context.get_merchant_account().get_id();

    if let Some(payout_link) = &req.payout_link {
        if *payout_link {
            validate_payout_link_request(req)?;
        }
    };

    // Merchant ID
    let predicate = req.merchant_id.as_ref().map(|mid| mid != merchant_id);
    utils::when(predicate.unwrap_or(false), || {
        Err(report!(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "merchant_id".to_string(),
            expected_format: "merchant_id from merchant account".to_string(),
        })
        .attach_printable("invalid merchant_id in request"))
    })?;

    // Payout ID
    let db: &dyn StorageInterface = &*state.store;
    let payout_id = match req.payout_id.as_ref() {
        Some(provided_payout_id) => provided_payout_id.clone(),
        None => id_type::PayoutId::generate(),
    };

    match validate_uniqueness_of_payout_id_against_merchant_id(
        db,
        &payout_id,
        merchant_id,
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await
    .attach_printable_lazy(|| {
        format!(
            "Unique violation while checking payout_id: {payout_id:?} against merchant_id: {merchant_id:?}"
        )
    })? {
        Some(_) => Err(report!(errors::ApiErrorResponse::DuplicatePayout {
            payout_id: payout_id.clone()
        })),
        None => Ok(()),
    }?;

    // Fetch customer details (merge of loose fields + customer object) and create DB entry
    let customer_in_request = helpers::get_customer_details_from_request(req);
    let customer = if customer_in_request.customer_id.is_some()
        || customer_in_request.name.is_some()
        || customer_in_request.email.is_some()
        || customer_in_request.phone.is_some()
        || customer_in_request.phone_country_code.is_some()
    {
        helpers::get_or_create_customer_details(state, &customer_in_request, merchant_context)
            .await?
    } else {
        None
    };

    #[cfg(feature = "v1")]
    let profile_id = core_utils::get_profile_id_from_business_details(
        &state.into(),
        req.business_country,
        req.business_label.as_ref(),
        merchant_context,
        req.profile_id.as_ref(),
        &*state.store,
        false,
    )
    .await?;

    #[cfg(feature = "v2")]
    // Profile id will be mandatory in v2 in the request / headers
    let profile_id = req
        .profile_id
        .clone()
        .ok_or(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "profile_id",
        })
        .attach_printable("Profile id is a mandatory parameter")?;

    let payment_method: Option<PaymentMethod> =
        match (req.payout_token.as_ref(), req.payout_method_id.clone()) {
            (Some(_), Some(_)) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                message: "Only one of payout_method_id or payout_token should be provided."
                    .to_string(),
            })),
            (None, Some(payment_method_id)) => match customer.as_ref() {
                Some(customer) => {
                    let payment_method = db
                        .find_payment_method(
                            &state.into(),
                            merchant_context.get_merchant_key_store(),
                            &payment_method_id,
                            merchant_context.get_merchant_account().storage_scheme,
                        )
                        .await
                        .change_context(errors::ApiErrorResponse::PaymentMethodNotFound)
                        .attach_printable("Unable to find payment method")?;

                    utils::when(payment_method.customer_id != customer.customer_id, || {
                        Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                        message: "Payment method does not belong to this customer_id".to_string(),
                    })
                    .attach_printable(
                        "customer_id in payment_method does not match with customer_id in request",
                    ))
                    })?;
                    Ok(Some(payment_method))
                }
                None => Err(report!(errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "customer_id when payment_method_id is passed",
                })),
            },
            _ => Ok(None),
        }?;

    // payout_token
    let payout_method_data = match (
        req.payout_token.as_ref(),
        customer.as_ref(),
        payment_method.as_ref(),
    ) {
        (Some(_), None, _) => Err(report!(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "customer or customer_id when payout_token is provided"
        })),
        (Some(payout_token), Some(customer), _) => {
            helpers::make_payout_method_data(
                state,
                req.payout_method_data.as_ref(),
                Some(payout_token),
                &customer.customer_id,
                merchant_context.get_merchant_account().get_id(),
                req.payout_type,
                merchant_context.get_merchant_key_store(),
                None,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
        }
        (_, Some(_), Some(payment_method)) => {
            // Check if we have a stored transfer_method_id first
            if payment_method
                .get_common_mandate_reference()
                .ok()
                .and_then(|common_mandate_ref| common_mandate_ref.payouts)
                .map(|payouts_mandate_ref| !payouts_mandate_ref.0.is_empty())
                .unwrap_or(false)
            {
                Ok(None)
            } else {
                // No transfer_method_id available, proceed with vault fetch for raw card details
                match get_pm_list_context(
                    state,
                    payment_method
                        .payment_method
                        .as_ref()
                        .get_required_value("payment_method_id")?,
                    merchant_context.get_merchant_key_store(),
                    payment_method,
                    None,
                    false,
                    true,
                    merchant_context,
                )
                .await?
                {
                    Some(pm) => match (pm.card_details, pm.bank_transfer_details) {
                        (Some(card), _) => Ok(Some(payouts::PayoutMethodData::Card(
                            api_models::payouts::CardPayout {
                                card_number: card.card_number.get_required_value("card_number")?,
                                card_holder_name: card.card_holder_name,
                                expiry_month: card
                                    .expiry_month
                                    .get_required_value("expiry_month")?,
                                expiry_year: card.expiry_year.get_required_value("expiry_year")?,
                            },
                        ))),
                        (_, Some(bank)) => Ok(Some(payouts::PayoutMethodData::Bank(bank))),
                        _ => Ok(None),
                    },
                    None => Ok(None),
                }
            }
        }
        _ => Ok(None),
    }?;

    Ok((
        payout_id,
        payout_method_data,
        profile_id,
        customer,
        payment_method,
    ))
}

pub fn validate_payout_link_request(
    req: &payouts::PayoutCreateRequest,
) -> Result<(), errors::ApiErrorResponse> {
    if req.confirm.unwrap_or(false) {
        return Err(errors::ApiErrorResponse::InvalidRequestData {
            message: "cannot confirm a payout while creating a payout link".to_string(),
        });
    }

    if req.customer_id.is_none() {
        return Err(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "customer or customer_id when payout_link is true",
        });
    }

    Ok(())
}

#[cfg(feature = "olap")]
pub(super) fn validate_payout_list_request(
    req: &payouts::PayoutListConstraints,
) -> CustomResult<(), errors::ApiErrorResponse> {
    use common_utils::consts::PAYOUTS_LIST_MAX_LIMIT_GET;

    utils::when(
        req.limit > PAYOUTS_LIST_MAX_LIMIT_GET || req.limit < 1,
        || {
            Err(errors::ApiErrorResponse::InvalidRequestData {
                message: format!("limit should be in between 1 and {PAYOUTS_LIST_MAX_LIMIT_GET}"),
            })
        },
    )?;
    Ok(())
}

#[cfg(feature = "olap")]
pub(super) fn validate_payout_list_request_for_joins(
    limit: u32,
) -> CustomResult<(), errors::ApiErrorResponse> {
    use common_utils::consts::PAYOUTS_LIST_MAX_LIMIT_POST;

    utils::when(!(1..=PAYOUTS_LIST_MAX_LIMIT_POST).contains(&limit), || {
        Err(errors::ApiErrorResponse::InvalidRequestData {
            message: format!("limit should be in between 1 and {PAYOUTS_LIST_MAX_LIMIT_POST}"),
        })
    })?;
    Ok(())
}

pub fn validate_payout_link_render_request_and_get_allowed_domains(
    request_headers: &header::HeaderMap,
    payout_link: &PayoutLink,
) -> RouterResult<HashSet<String>> {
    let link_id = payout_link.link_id.to_owned();
    let link_data = payout_link.link_data.to_owned();

    let is_test_mode_enabled = link_data.test_mode.unwrap_or(false);

    match (router_env_which(), is_test_mode_enabled) {
        // Throw error in case test_mode was enabled in production
        (Env::Production, true) => Err(report!(errors::ApiErrorResponse::LinkConfigurationError {
            message: "test_mode cannot be true for rendering payout_links in production"
                .to_string()
        })),
        // Skip all validations when test mode is enabled in non prod env
        (_, true) => Ok(HashSet::new()),
        // Otherwise, perform validations
        (_, false) => {
            // Fetch destination is "iframe"
            match request_headers.get("sec-fetch-dest").and_then(|v| v.to_str().ok()) {
                Some("iframe") => Ok(()),
                Some(requestor) => Err(report!(errors::ApiErrorResponse::AccessForbidden {
                    resource: "payout_link".to_string(),
                }))
                .attach_printable_lazy(|| {
                    format!(
                        "Access to payout_link [{link_id}] is forbidden when requested through {requestor}",

                    )
                }),
                None => Err(report!(errors::ApiErrorResponse::AccessForbidden {
                    resource: "payout_link".to_string(),
                }))
                .attach_printable_lazy(|| {
                    format!(
                        "Access to payout_link [{link_id}] is forbidden when sec-fetch-dest is not present in request headers",

                    )
                }),
            }?;

            // Validate origin / referer
            let domain_in_req = {
                let origin_or_referer = request_headers
                    .get("origin")
                    .or_else(|| request_headers.get("referer"))
                    .and_then(|v| v.to_str().ok())
                    .ok_or_else(|| {
                        report!(errors::ApiErrorResponse::AccessForbidden {
                            resource: "payout_link".to_string(),
                        })
                    })
                    .attach_printable_lazy(|| {
                        format!(
                            "Access to payout_link [{link_id}] is forbidden when origin or referer is not present in request headers",

                        )
                    })?;

                let url = Url::parse(origin_or_referer)
                    .map_err(|_| {
                        report!(errors::ApiErrorResponse::AccessForbidden {
                            resource: "payout_link".to_string(),
                        })
                    })
                    .attach_printable_lazy(|| {
                        format!("Invalid URL found in request headers {origin_or_referer}")
                    })?;

                url.host_str()
                    .and_then(|host| url.port().map(|port| format!("{host}:{port}")))
                    .or_else(|| url.host_str().map(String::from))
                    .ok_or_else(|| {
                        report!(errors::ApiErrorResponse::AccessForbidden {
                            resource: "payout_link".to_string(),
                        })
                    })
                    .attach_printable_lazy(|| {
                        format!("host or port not found in request headers {url:?}")
                    })?
            };

            if validate_domain_against_allowed_domains(
                &domain_in_req,
                link_data.allowed_domains.clone(),
            ) {
                Ok(link_data.allowed_domains)
            } else {
                Err(report!(errors::ApiErrorResponse::AccessForbidden {
                    resource: "payout_link".to_string(),
                }))
                .attach_printable_lazy(|| {
                    format!(
                        "Access to payout_link [{link_id}] is forbidden from requestor - {domain_in_req}",

                    )
                })
            }
        }
    }
}


impl ConnectorValidation for Forte {}


pub struct RedsysPaymentsResponse {
    #[serde(rename = "Ds_Order")]
    ds_order: String,
    #[serde(rename = "Ds_EMV3DS")]
    ds_emv3ds: Option<RedsysEmv3DSData>,
    #[serde(rename = "Ds_Card_PSD2")]
    ds_card_psd2: Option<CardPSD2>,
    #[serde(rename = "Ds_Response")]
    ds_response: Option<DsResponse>,
    #[serde(rename = "Ds_AuthorisationCode")]
    ds_authorisation_code: Option<String>,
}


pub struct Braintree {
    amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
    amount_converter_webhooks: &'static (dyn AmountConvertor<Output = StringMinorUnit> + Sync),
}


OpenAPI Block Path: components.schemas.ConnectorStatus
{
  "type": "string",
  "enum": [
    "inactive",
    "active"
  ]
}


File: crates/hyperswitch_connectors/src/connectors/stax/transformers.rs
 Public structs: 17

use common_enums::enums;
use common_utils::pii::Email;
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{BankDebitData, PaymentMethodData},
    router_data::{ConnectorAuthType, PaymentMethodToken, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types,
};
use hyperswitch_interfaces::{api, errors};
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    unimplemented_payment_method,
    utils::{
        self, missing_field_err, CardData as CardDataUtil, PaymentsAuthorizeRequestData,
        RouterData as _,
    },
};

#[derive(Debug, Serialize)]
pub struct StaxRouterData<T> {
    pub amount: f64,
    pub router_data: T,
}

impl<T> TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for StaxRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T),
    ) -> Result<Self, Self::Error> {
        let amount = utils::get_amount_as_f64(currency_unit, amount, currency)?;
        Ok(Self {
            amount,
            router_data: item,
        })
    }
}

#[derive(Debug, Serialize)]
pub struct StaxPaymentsRequestMetaData {
    tax: i64,
}

#[derive(Debug, Serialize)]
pub struct StaxPaymentsRequest {
    payment_method_id: Secret<String>,
    total: f64,
    is_refundable: bool,
    pre_auth: bool,
    meta: StaxPaymentsRequestMetaData,
    idempotency_id: Option<String>,
}

impl TryFrom<&StaxRouterData<&types::PaymentsAuthorizeRouterData>> for StaxPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &StaxRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        if item.router_data.request.currency != enums::Currency::USD {
            Err(errors::ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Stax"),
            ))?
        }
        let total = item.amount;

        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(_) => {
                let pm_token = item.router_data.get_payment_method_token()?;
                let pre_auth = !item.router_data.request.is_auto_capture()?;
                Ok(Self {
                    meta: StaxPaymentsRequestMetaData { tax: 0 },
                    total,
                    is_refundable: true,
                    pre_auth,
                    payment_method_id: match pm_token {
                        PaymentMethodToken::Token(token) => token,
                        PaymentMethodToken::ApplePayDecrypt(_) => Err(
                            unimplemented_payment_method!("Apple Pay", "Simplified", "Stax"),
                        )?,
                        PaymentMethodToken::PazeDecrypt(_) => {
                            Err(unimplemented_payment_method!("Paze", "Stax"))?
                        }
                        PaymentMethodToken::GooglePayDecrypt(_) => {
                            Err(unimplemented_payment_method!("Google Pay", "Stax"))?
                        }
                    },
                    idempotency_id: Some(item.router_data.connector_request_reference_id.clone()),
                })
            }
            PaymentMethodData::BankDebit(BankDebitData::AchBankDebit { .. }) => {
                let pm_token = item.router_data.get_payment_method_token()?;
                let pre_auth = !item.router_data.request.is_auto_capture()?;
                Ok(Self {
                    meta: StaxPaymentsRequestMetaData { tax: 0 },
                    total,
                    is_refundable: true,
                    pre_auth,
                    payment_method_id: match pm_token {
                        PaymentMethodToken::Token(token) => token,
                        PaymentMethodToken::ApplePayDecrypt(_) => Err(
                            unimplemented_payment_method!("Apple Pay", "Simplified", "Stax"),
                        )?,
                        PaymentMethodToken::PazeDecrypt(_) => {
                            Err(unimplemented_payment_method!("Paze", "Stax"))?
                        }
                        PaymentMethodToken::GooglePayDecrypt(_) => {
                            Err(unimplemented_payment_method!("Google Pay", "Stax"))?
                        }
                    },
                    idempotency_id: Some(item.router_data.connector_request_reference_id.clone()),
                })
            }
            PaymentMethodData::BankDebit(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("Stax"),
                ))?
            }
        }
    }
}

// Auth Struct
pub struct StaxAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for StaxAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                api_key: api_key.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Serialize)]
pub struct StaxCustomerRequest {
    #[serde(skip_serializing_if = "Option::is_none")]
    email: Option<Email>,
    #[serde(skip_serializing_if = "Option::is_none")]
    firstname: Option<Secret<String>>,
}

impl TryFrom<&types::ConnectorCustomerRouterData> for StaxCustomerRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::ConnectorCustomerRouterData) -> Result<Self, Self::Error> {
        if item.request.email.is_none() && item.request.name.is_none() {
            Err(errors::ConnectorError::MissingRequiredField {
                field_name: "email or name",
            }
            .into())
        } else {
            Ok(Self {
                email: item.request.email.to_owned(),
                firstname: item.request.name.to_owned(),
            })
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct StaxCustomerResponse {
    id: Secret<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, StaxCustomerResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, StaxCustomerResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(PaymentsResponseData::ConnectorCustomerResponse {
                connector_customer_id: item.response.id.expose(),
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
pub struct StaxTokenizeData {
    person_name: Secret<String>,
    card_number: cards::CardNumber,
    card_exp: Secret<String>,
    card_cvv: Secret<String>,
    customer_id: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct StaxBankTokenizeData {
    person_name: Secret<String>,
    bank_account: Secret<String>,
    bank_routing: Secret<String>,
    bank_name: common_enums::BankNames,
    bank_type: common_enums::BankType,
    bank_holder_type: common_enums::BankHolderType,
    customer_id: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(tag = "method")]
#[serde(rename_all = "lowercase")]
pub enum StaxTokenRequest {
    Card(StaxTokenizeData),
    Bank(StaxBankTokenizeData),
}

impl TryFrom<&types::TokenizationRouterData> for StaxTokenRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::TokenizationRouterData) -> Result<Self, Self::Error> {
        let customer_id = item.get_connector_customer_id()?;
        match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(card_data) => {
                let stax_card_data = StaxTokenizeData {
                    card_exp: card_data
                        .get_card_expiry_month_year_2_digit_with_delimiter("".to_string())?,
                    person_name: item
                        .get_optional_billing_full_name()
                        .unwrap_or(Secret::new("".to_string())),
                    card_number: card_data.card_number,
                    card_cvv: card_data.card_cvc,
                    customer_id: Secret::new(customer_id),
                };
                Ok(Self::Card(stax_card_data))
            }
            PaymentMethodData::BankDebit(BankDebitData::AchBankDebit {
                account_number,
                routing_number,
                bank_name,
                bank_type,
                bank_holder_type,
                ..
            }) => {
                let stax_bank_data = StaxBankTokenizeData {
                    person_name: item.get_billing_full_name()?,
                    bank_account: account_number,
                    bank_routing: routing_number,
                    bank_name: bank_name.ok_or_else(missing_field_err("bank_name"))?,
                    bank_type: bank_type.ok_or_else(missing_field_err("bank_type"))?,
                    bank_holder_type: bank_holder_type
                        .ok_or_else(missing_field_err("bank_holder_type"))?,
                    customer_id: Secret::new(customer_id),
                };
                Ok(Self::Bank(stax_bank_data))
            }
            PaymentMethodData::BankDebit(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("Stax"),
                ))?
            }
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct StaxTokenResponse {
    id: Secret<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, StaxTokenResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, StaxTokenResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(PaymentsResponseData::TokenizationResponse {
                token: item.response.id.expose(),
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum StaxPaymentResponseTypes {
    Charge,
    PreAuth,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct StaxChildCapture {
    id: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct StaxPaymentsResponse {
    success: bool,
    id: String,
    is_captured: i8,
    is_voided: bool,
    child_captures: Vec<StaxChildCapture>,
    #[serde(rename = "type")]
    payment_response_type: StaxPaymentResponseTypes,
    idempotency_id: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct StaxMetaData {
    pub capture_id: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, StaxPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, StaxPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let mut connector_metadata = None;
        let mut status = match item.response.success {
            true => match item.response.payment_response_type {
                StaxPaymentResponseTypes::Charge => enums::AttemptStatus::Charged,
                StaxPaymentResponseTypes::PreAuth => match item.response.is_captured {
                    0 => enums::AttemptStatus::Authorized,
                    _ => {
                        connector_metadata =
                            item.response.child_captures.first().map(|child_captures| {
                                serde_json::json!(StaxMetaData {
                                    capture_id: child_captures.id.clone()
                                })
                            });
                        enums::AttemptStatus::Charged
                    }
                },
            },
            false => enums::AttemptStatus::Failure,
        };
        if item.response.is_voided {
            status = enums::AttemptStatus::Voided;
        }

        Ok(Self {
            status,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata,
                network_txn_id: None,
                connector_response_reference_id: Some(
                    item.response.idempotency_id.unwrap_or(item.response.id),
                ),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StaxCaptureRequest {
    total: Option<f64>,
}

impl TryFrom<&StaxRouterData<&types::PaymentsCaptureRouterData>> for StaxCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &StaxRouterData<&types::PaymentsCaptureRouterData>,
    ) -> Result<Self, Self::Error> {
        let total = item.amount;
        Ok(Self { total: Some(total) })
    }
}

// REFUND :
// Type definition for RefundRequest
#[derive(Debug, Serialize)]
pub struct StaxRefundRequest {
    pub total: f64,
}

impl<F> TryFrom<&StaxRouterData<&types::RefundsRouterData<F>>> for StaxRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &StaxRouterData<&types::RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        Ok(Self { total: item.amount })
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct ChildTransactionsInResponse {
    id: String,
    success: bool,
    created_at: String,
    total: f64,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RefundResponse {
    id: String,
    success: bool,
    child_transactions: Vec<ChildTransactionsInResponse>,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>>
    for types::RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_amount = utils::to_currency_base_unit_asf64(
            item.data.request.refund_amount,
            item.data.request.currency,
        )
        .change_context(errors::ConnectorError::ResponseHandlingFailed)?;
        let filtered_txn: Vec<&ChildTransactionsInResponse> = item
            .response
            .child_transactions
            .iter()
            .filter(|txn| txn.total == refund_amount)
            .collect();

        let mut refund_txn = filtered_txn
            .first()
            .ok_or(errors::ConnectorError::ResponseHandlingFailed)?;

        for child in filtered_txn.iter() {
            if child.created_at > refund_txn.created_at {
                refund_txn = child;
            }
        }

        let refund_status = match refund_txn.success {
            true => enums::RefundStatus::Success,
            false => enums::RefundStatus::Failure,
        };

        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: refund_txn.id.clone(),
                refund_status,
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for types::RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = match item.response.success {
            true => enums::RefundStatus::Success,
            false => enums::RefundStatus::Failure,
        };
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id,
                refund_status,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StaxWebhookEventType {
    PreAuth,
    Capture,
    Charge,
    Void,
    Refund,
    #[serde(other)]
    Unknown,
}

#[derive(Debug, Deserialize)]
pub struct StaxWebhookBody {
    #[serde(rename = "type")]
    pub transaction_type: StaxWebhookEventType,
    pub id: String,
    pub auth_id: Option<String>,
    pub success: bool,
}


pub struct PaystackPaymentsResponseData {
    status: bool,
    message: String,
    data: PaystackEftRedirect,
}


pub struct SwitchProfileRequest {
    pub profile_id: id_type::ProfileId,
}


pub struct Shift4 {
    amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
}


pub struct Details {
    pub field: String,
    pub reason: String,
}


impl api::ConnectorAccessToken for Bankofamerica {}

impl api::ConnectorAccessToken for for Bankofamerica


impl api::MandateSetup for Chargebee {}

impl api::MandateSetup for for Chargebee


pub fn update_payment_method_data(
        &mut self,
        payment_method_data: payment_method_data::PaymentMethodData,
    ) {
        self.payment_method_data = Some(payment_method_data);
    }


pub struct GocardlessBankAccountRequest {
    customer_bank_accounts: CustomerBankAccounts,
}


pub struct BankOfAmericaStandardErrorResponse {
    pub error_information: Option<ErrorInformation>,
    pub status: Option<String>,
    pub message: Option<String>,
    pub reason: Option<String>,
    pub details: Option<Vec<Details>>,
}


pub struct CryptopayAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) api_secret: Secret<String>,
}


pub struct ChargebeePaymentsResponse {
    status: ChargebeePaymentStatus,
    id: String,
}


pub struct Plaid;


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

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

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

impl ConnectorSpecifications for for Barclaycard


OpenAPI Block Path: components.schemas.GooglePaySessionResponse
{
  "type": "object",
  "required": [
    "merchant_info",
    "shipping_address_required",
    "email_required",
    "shipping_address_parameters",
    "allowed_payment_methods",
    "transaction_info",
    "delayed_session_token",
    "connector",
    "sdk_next_action"
  ],
  "properties": {
    "merchant_info": {
      "$ref": "#/components/schemas/GpayMerchantInfo"
    },
    "shipping_address_required": {
      "type": "boolean",
      "description": "Is shipping address required"
    },
    "email_required": {
      "type": "boolean",
      "description": "Is email required"
    },
    "shipping_address_parameters": {
      "$ref": "#/components/schemas/GpayShippingAddressParameters"
    },
    "allowed_payment_methods": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/GpayAllowedPaymentMethods"
      },
      "description": "List of the allowed payment meythods"
    },
    "transaction_info": {
      "$ref": "#/components/schemas/GpayTransactionInfo"
    },
    "delayed_session_token": {
      "type": "boolean",
      "description": "Identifier for the delayed session response"
    },
    "connector": {
      "type": "string",
      "description": "The name of the connector"
    },
    "sdk_next_action": {
      "$ref": "#/components/schemas/SdkNextAction"
    },
    "secrets": {
      "allOf": [
        {
          "$ref": "#/components/schemas/SecretInfoToInitiateSdk"
        }
      ],
      "nullable": true
    }
  }
}


impl Drop for RdKafkaProducer {
    fn drop(&mut self) {
        // Flush the producer to send any pending messages
        match self.0.flush(rdkafka::util::Timeout::After(
            std::time::Duration::from_secs(5),
        )) {
            Ok(_) => router_env::logger::info!("Kafka events flush Successful"),
            Err(error) => router_env::logger::error!("Failed to flush Kafka Events {error:?}"),
        }
    }
}


pub struct XenditMultipleSplitRequest {
    /// Name to identify split rule. Not required to be unique. Typically based on transaction and/or sub-merchant types.
    pub name: String,
    /// Description to identify fee rule
    pub description: String,
    /// The sub-account user-id that you want to make this transaction for.
    pub for_user_id: Option<String>,
    /// Array of objects that define how the platform wants to route the fees and to which accounts.
    pub routes: Vec<XenditSplitRoute>,
}


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

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }
    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.riskified.base_url.as_ref()
    }

    #[cfg(feature = "frm")]
    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        use hyperswitch_interfaces::consts::NO_ERROR_CODE;

        let response: riskified::ErrorResponse = res
            .response
            .parse_struct("ErrorResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            attempt_status: None,
            code: NO_ERROR_CODE.to_string(),
            message: response.error.message.clone(),
            reason: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


OpenAPI Block Path: components.schemas.SessionTokenInfo
{
  "allOf": [
    {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentProcessingDetailsAt"
        }
      ],
      "nullable": true
    },
    {
      "type": "object",
      "required": [
        "certificate",
        "certificate_keys",
        "merchant_identifier",
        "display_name",
        "initiative"
      ],
      "properties": {
        "certificate": {
          "type": "string"
        },
        "certificate_keys": {
          "type": "string"
        },
        "merchant_identifier": {
          "type": "string"
        },
        "display_name": {
          "type": "string"
        },
        "initiative": {
          "$ref": "#/components/schemas/ApplepayInitiative"
        },
        "initiative_context": {
          "type": "string",
          "nullable": true
        },
        "merchant_business_country": {
          "allOf": [
            {
              "$ref": "#/components/schemas/CountryAlpha2"
            }
          ],
          "nullable": true
        }
      }
    }
  ]
}


        status: common_enums::PaymentMethodStatus,
        limit: Option<i64>,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<domain::PaymentMethod>, errors::StorageError> {
        self.diesel_store
            .find_payment_method_by_customer_id_merchant_id_status(
                state,
                key_store,
                customer_id,
                merchant_id,
                status,
                limit,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn find_payment_method_by_global_customer_id_merchant_id_status(
        &self,
        state: &KeyManagerState,
        key_store: &domain::MerchantKeyStore,
        customer_id: &id_type::GlobalCustomerId,
        merchant_id: &id_type::MerchantId,
        status: common_enums::PaymentMethodStatus,
        limit: Option<i64>,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<domain::PaymentMethod>, errors::StorageError> {
        self.diesel_store
            .find_payment_method_by_global_customer_id_merchant_id_status(
                state,
                key_store,
                customer_id,
                merchant_id,
                status,
                limit,
                storage_scheme,
            )
            .await
    }

    #[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, errors::StorageError> {
        self.diesel_store
            .get_payment_method_count_by_customer_id_merchant_id_status(
                customer_id,
                merchant_id,
                status,
            )
            .await
    }

    async fn get_payment_method_count_by_merchant_id_status(
        &self,
        merchant_id: &id_type::MerchantId,
        status: common_enums::PaymentMethodStatus,
    ) -> CustomResult<i64, errors::StorageError> {
        self.diesel_store
            .get_payment_method_count_by_merchant_id_status(merchant_id, status)
            .await
    }

    #[cfg(feature = "v1")]
    async fn find_payment_method_by_locker_id(
        &self,
        state: &KeyManagerState,
        key_store: &domain::MerchantKeyStore,
        locker_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
        self.diesel_store
            .find_payment_method_by_locker_id(state, key_store, locker_id, storage_scheme)
            .await
    }

    async fn insert_payment_method(
        &self,
        state: &KeyManagerState,
        key_store: &domain::MerchantKeyStore,
        m: domain::PaymentMethod,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
        self.diesel_store
            .insert_payment_method(state, key_store, m, storage_scheme)
            .await
    }

    async fn update_payment_method(
        &self,
        state: &KeyManagerState,
        key_store: &domain::MerchantKeyStore,
        payment_method: domain::PaymentMethod,
        payment_method_update: storage::PaymentMethodUpdate,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
        self.diesel_store
            .update_payment_method(
                state,
                key_store,
                payment_method,
                payment_method_update,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v1")]
    async fn delete_payment_method_by_merchant_id_payment_method_id(
        &self,
        state: &KeyManagerState,
        key_store: &domain::MerchantKeyStore,
        merchant_id: &id_type::MerchantId,
        payment_method_id: &str,
    ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
        self.diesel_store
            .delete_payment_method_by_merchant_id_payment_method_id(
                state,
                key_store,
                merchant_id,
                payment_method_id,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn delete_payment_method(
        &self,
        state: &KeyManagerState,
        key_store: &domain::MerchantKeyStore,
        payment_method: domain::PaymentMethod,
    ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
        self.diesel_store
            .delete_payment_method(state, key_store, payment_method)
            .await
    }

    #[cfg(feature = "v2")]
    async fn find_payment_method_by_fingerprint_id(
        &self,
        state: &KeyManagerState,
        key_store: &domain::MerchantKeyStore,
        fingerprint_id: &str,
    ) -> CustomResult<domain::PaymentMethod, errors::StorageError> {
        self.diesel_store
            .find_payment_method_by_fingerprint_id(state, key_store, fingerprint_id)
            .await
    }
}

#[cfg(not(feature = "payouts"))]
impl PayoutAttemptInterface for KafkaStore {}

#[cfg(feature = "payouts")]
#[async_trait::async_trait]
impl PayoutAttemptInterface for KafkaStore {
    type Error = errors::StorageError;

    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
        &self,
        merchant_id: &id_type::MerchantId,
        merchant_order_reference_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::PayoutAttempt, errors::StorageError> {
        self.diesel_store
            .find_payout_attempt_by_merchant_id_merchant_order_reference_id(
                merchant_id,
                merchant_order_reference_id,
                storage_scheme,
            )
            .await
    }

    async fn find_payout_attempt_by_merchant_id_payout_attempt_id(
        &self,
        merchant_id: &id_type::MerchantId,
        payout_attempt_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::PayoutAttempt, errors::StorageError> {
        self.diesel_store
            .find_payout_attempt_by_merchant_id_payout_attempt_id(
                merchant_id,
                payout_attempt_id,
                storage_scheme,
            )
            .await
    }

    async fn find_payout_attempt_by_merchant_id_connector_payout_id(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_payout_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::PayoutAttempt, errors::StorageError> {
        self.diesel_store
            .find_payout_attempt_by_merchant_id_connector_payout_id(
                merchant_id,
                connector_payout_id,
                storage_scheme,
            )
            .await
    }

    async fn update_payout_attempt(
        &self,
        this: &storage::PayoutAttempt,
        payout_attempt_update: storage::PayoutAttemptUpdate,
        payouts: &storage::Payouts,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::PayoutAttempt, errors::StorageError> {
        let updated_payout_attempt = self
            .diesel_store
            .update_payout_attempt(this, payout_attempt_update, payouts, storage_scheme)
            .await?;
        if let Err(err) = self
            .kafka_producer
            .log_payout(
                &KafkaPayout::from_storage(payouts, &updated_payout_attempt),
                Some(KafkaPayout::from_storage(payouts, this)),
                self.tenant_id.clone(),
            )
            .await
        {
            logger::error!(message="Failed to update analytics entry for Payouts {payouts:?}\n{updated_payout_attempt:?}", error_message=?err);
        };

        Ok(updated_payout_attempt)
    }

    async fn insert_payout_attempt(
        &self,
        payout_attempt: storage::PayoutAttemptNew,
        payouts: &storage::Payouts,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::PayoutAttempt, errors::StorageError> {
        let payout_attempt_new = self
            .diesel_store
            .insert_payout_attempt(payout_attempt, payouts, storage_scheme)
            .await?;
        if let Err(err) = self
            .kafka_producer
            .log_payout(
                &KafkaPayout::from_storage(payouts, &payout_attempt_new),
                None,
                self.tenant_id.clone(),
            )
            .await
        {
            logger::error!(message="Failed to add analytics entry for Payouts {payouts:?}\n{payout_attempt_new:?}", error_message=?err);
        };

        Ok(payout_attempt_new)
    }

    async fn get_filters_for_payouts(
        &self,
        payouts: &[hyperswitch_domain_models::payouts::payouts::Payouts],
        merchant_id: &id_type::MerchantId,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<
        hyperswitch_domain_models::payouts::payout_attempt::PayoutListFilters,
        errors::StorageError,
    > {
        self.diesel_store
            .get_filters_for_payouts(payouts, merchant_id, storage_scheme)
            .await
    }
}

#[cfg(not(feature = "payouts"))]
impl PayoutsInterface for KafkaStore {}

#[cfg(feature = "payouts")]
#[async_trait::async_trait]
impl PayoutsInterface for KafkaStore {
    type Error = errors::StorageError;
    async fn find_payout_by_merchant_id_payout_id(
        &self,
        merchant_id: &id_type::MerchantId,
        payout_id: &id_type::PayoutId,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Payouts, errors::StorageError> {
        self.diesel_store
            .find_payout_by_merchant_id_payout_id(merchant_id, payout_id, storage_scheme)
            .await
    }

    async fn update_payout(
        &self,
        this: &storage::Payouts,
        payout_update: storage::PayoutsUpdate,
        payout_attempt: &storage::PayoutAttempt,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Payouts, errors::StorageError> {
        let payout = self
            .diesel_store
            .update_payout(this, payout_update, payout_attempt, storage_scheme)
            .await?;
        if let Err(err) = self
            .kafka_producer
            .log_payout(
                &KafkaPayout::from_storage(&payout, payout_attempt),
                Some(KafkaPayout::from_storage(this, payout_attempt)),
                self.tenant_id.clone(),
            )
            .await
        {
            logger::error!(message="Failed to update analytics entry for Payouts {payout:?}\n{payout_attempt:?}", error_message=?err);
        };
        Ok(payout)
    }

    async fn insert_payout(
        &self,
        payout: storage::PayoutsNew,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Payouts, errors::StorageError> {
        self.diesel_store
            .insert_payout(payout, storage_scheme)
            .await
    }

    async fn find_optional_payout_by_merchant_id_payout_id(
        &self,
        merchant_id: &id_type::MerchantId,
        payout_id: &id_type::PayoutId,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Option<storage::Payouts>, errors::StorageError> {
        self.diesel_store
            .find_optional_payout_by_merchant_id_payout_id(merchant_id, payout_id, storage_scheme)
            .await
    }

    #[cfg(feature = "olap")]
    async fn filter_payouts_by_constraints(
        &self,
        merchant_id: &id_type::MerchantId,
        filters: &hyperswitch_domain_models::payouts::PayoutFetchConstraints,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<storage::Payouts>, errors::StorageError> {
        self.diesel_store
            .filter_payouts_by_constraints(merchant_id, filters, storage_scheme)
            .await
    }

    #[cfg(feature = "olap")]
    async fn filter_payouts_and_attempts(
        &self,
        merchant_id: &id_type::MerchantId,
        filters: &hyperswitch_domain_models::payouts::PayoutFetchConstraints,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<
        Vec<(
            storage::Payouts,
            storage::PayoutAttempt,
            Option<diesel_models::Customer>,
            Option<diesel_models::Address>,
        )>,
        errors::StorageError,
    > {
        self.diesel_store
            .filter_payouts_and_attempts(merchant_id, filters, storage_scheme)
            .await
    }

    #[cfg(feature = "olap")]
    async fn filter_payouts_by_time_range_constraints(
        &self,
        merchant_id: &id_type::MerchantId,
        time_range: &common_utils::types::TimeRange,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<storage::Payouts>, errors::StorageError> {
        self.diesel_store
            .filter_payouts_by_time_range_constraints(merchant_id, time_range, storage_scheme)
            .await
    }

    #[cfg(feature = "olap")]
    async fn get_total_count_of_filtered_payouts(
        &self,
        merchant_id: &id_type::MerchantId,
        active_payout_ids: &[id_type::PayoutId],
        connector: Option<Vec<api_models::enums::PayoutConnectors>>,
        currency: Option<Vec<enums::Currency>>,
        status: Option<Vec<enums::PayoutStatus>>,
        payout_method: Option<Vec<enums::PayoutType>>,
    ) -> CustomResult<i64, errors::StorageError> {
        self.diesel_store
            .get_total_count_of_filtered_payouts(
                merchant_id,
                active_payout_ids,
                connector,
                currency,
                status,
                payout_method,
            )
            .await
    }

    #[cfg(feature = "olap")]
    async fn filter_active_payout_ids_by_constraints(
        &self,
        merchant_id: &id_type::MerchantId,
        constraints: &hyperswitch_domain_models::payouts::PayoutFetchConstraints,
    ) -> CustomResult<Vec<id_type::PayoutId>, errors::StorageError> {
        self.diesel_store
            .filter_active_payout_ids_by_constraints(merchant_id, constraints)
            .await
    }
}

#[async_trait::async_trait]
impl ProcessTrackerInterface for KafkaStore {
    async fn reinitialize_limbo_processes(
        &self,
        ids: Vec<String>,
        schedule_time: PrimitiveDateTime,
    ) -> CustomResult<usize, errors::StorageError> {
        self.diesel_store
            .reinitialize_limbo_processes(ids, schedule_time)
            .await
    }

    async fn find_process_by_id(
        &self,
        id: &str,
    ) -> CustomResult<Option<storage::ProcessTracker>, errors::StorageError> {
        self.diesel_store.find_process_by_id(id).await
    }

    async fn update_process(
        &self,
        this: storage::ProcessTracker,
        process: storage::ProcessTrackerUpdate,
    ) -> CustomResult<storage::ProcessTracker, errors::StorageError> {
        self.diesel_store.update_process(this, process).await
    }

    async fn process_tracker_update_process_status_by_ids(
        &self,
        task_ids: Vec<String>,
        task_update: storage::ProcessTrackerUpdate,
    ) -> CustomResult<usize, errors::StorageError> {
        self.diesel_store
            .process_tracker_update_process_status_by_ids(task_ids, task_update)
            .await
    }

    async fn insert_process(
        &self,
        new: storage::ProcessTrackerNew,
    ) -> CustomResult<storage::ProcessTracker, errors::StorageError> {
        self.diesel_store.insert_process(new).await
    }

    async fn reset_process(
        &self,
        this: storage::ProcessTracker,
        schedule_time: PrimitiveDateTime,
    ) -> CustomResult<(), errors::StorageError> {
        self.diesel_store.reset_process(this, schedule_time).await
    }

    async fn retry_process(
        &self,
        this: storage::ProcessTracker,
        schedule_time: PrimitiveDateTime,
    ) -> CustomResult<(), errors::StorageError> {
        self.diesel_store.retry_process(this, schedule_time).await
    }

    async fn finish_process_with_business_status(
        &self,
        this: storage::ProcessTracker,
        business_status: &'static str,
    ) -> CustomResult<(), errors::StorageError> {
        self.diesel_store
            .finish_process_with_business_status(this, business_status)
            .await
    }
    async fn find_processes_by_time_status(
        &self,
        time_lower_limit: PrimitiveDateTime,
        time_upper_limit: PrimitiveDateTime,
        status: ProcessTrackerStatus,
        limit: Option<i64>,
    ) -> CustomResult<Vec<storage::ProcessTracker>, errors::StorageError> {
        self.diesel_store
            .find_processes_by_time_status(time_lower_limit, time_upper_limit, status, limit)
            .await
    }
}

#[async_trait::async_trait]
impl CaptureInterface for KafkaStore {
    async fn insert_capture(
        &self,
        capture: storage::CaptureNew,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Capture, errors::StorageError> {
        self.diesel_store
            .insert_capture(capture, storage_scheme)
            .await
    }

    async fn update_capture_with_capture_id(
        &self,
        this: storage::Capture,
        capture: storage::CaptureUpdate,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<storage::Capture, errors::StorageError> {
        self.diesel_store
            .update_capture_with_capture_id(this, capture, storage_scheme)
            .await
    }

    async fn find_all_captures_by_merchant_id_payment_id_authorized_attempt_id(
        &self,
        merchant_id: &id_type::MerchantId,
        payment_id: &id_type::PaymentId,
        authorized_attempt_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<storage::Capture>, errors::StorageError> {
        self.diesel_store
            .find_all_captures_by_merchant_id_payment_id_authorized_attempt_id(
                merchant_id,
                payment_id,
                authorized_attempt_id,
                storage_scheme,
            )
            .await
    }
}

#[async_trait::async_trait]
impl RefundInterface for KafkaStore {
    #[cfg(feature = "v1")]
    async fn find_refund_by_internal_reference_id_merchant_id(
        &self,
        internal_reference_id: &str,
        merchant_id: &id_type::MerchantId,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<diesel_refund::Refund, errors::StorageError> {
        self.diesel_store
            .find_refund_by_internal_reference_id_merchant_id(
                internal_reference_id,
                merchant_id,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v1")]
    async fn find_refund_by_payment_id_merchant_id(
        &self,
        payment_id: &id_type::PaymentId,
        merchant_id: &id_type::MerchantId,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<diesel_refund::Refund>, errors::StorageError> {
        self.diesel_store
            .find_refund_by_payment_id_merchant_id(payment_id, merchant_id, storage_scheme)
            .await
    }

    #[cfg(feature = "v1")]
    async fn find_refund_by_merchant_id_refund_id(
        &self,
        merchant_id: &id_type::MerchantId,
        refund_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<diesel_refund::Refund, errors::StorageError> {
        self.diesel_store
            .find_refund_by_merchant_id_refund_id(merchant_id, refund_id, storage_scheme)
            .await
    }

    #[cfg(feature = "v1")]
    async fn find_refund_by_merchant_id_connector_refund_id_connector(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_refund_id: &str,
        connector: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<diesel_refund::Refund, errors::StorageError> {
        self.diesel_store
            .find_refund_by_merchant_id_connector_refund_id_connector(
                merchant_id,
                connector_refund_id,
                connector,
                storage_scheme,
            )
            .await
    }

    async fn update_refund(
        &self,
        this: diesel_refund::Refund,
        refund: diesel_refund::RefundUpdate,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<diesel_refund::Refund, errors::StorageError> {
        let refund = self
            .diesel_store
            .update_refund(this.clone(), refund, storage_scheme)
            .await?;

        if let Err(er) = self
            .kafka_producer
            .log_refund(&refund, Some(this), self.tenant_id.clone())
            .await
        {
            logger::error!(message="Failed to insert analytics event for Refund Update {refund?}", error_message=?er);
        }
        Ok(refund)
    }

    async fn find_refund_by_merchant_id_connector_transaction_id(
        &self,
        merchant_id: &id_type::MerchantId,
        connector_transaction_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<diesel_refund::Refund>, errors::StorageError> {
        self.diesel_store
            .find_refund_by_merchant_id_connector_transaction_id(
                merchant_id,
                connector_transaction_id,
                storage_scheme,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn find_refund_by_id(
        &self,
        id: &id_type::GlobalRefundId,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<diesel_refund::Refund, errors::StorageError> {
        self.diesel_store
            .find_refund_by_id(id, storage_scheme)
            .await
    }

    async fn insert_refund(
        &self,
        new: diesel_refund::RefundNew,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<diesel_refund::Refund, errors::StorageError> {
        let refund = self.diesel_store.insert_refund(new, storage_scheme).await?;

        if let Err(er) = self
            .kafka_producer
            .log_refund(&refund, None, self.tenant_id.clone())
            .await
        {
            logger::error!(message="Failed to insert analytics event for Refund Create {refund?}", error_message=?er);
        }
        Ok(refund)
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn filter_refund_by_constraints(
        &self,
        merchant_id: &id_type::MerchantId,
        refund_details: &refunds::RefundListConstraints,
        storage_scheme: MerchantStorageScheme,
        limit: i64,
        offset: i64,
    ) -> CustomResult<Vec<diesel_refund::Refund>, errors::StorageError> {
        self.diesel_store
            .filter_refund_by_constraints(
                merchant_id,
                refund_details,
                storage_scheme,
                limit,
                offset,
            )
            .await
    }

    #[cfg(feature = "v2")]
    async fn filter_refund_by_constraints(
        &self,
        merchant_id: &id_type::MerchantId,
        refund_details: refunds::RefundListConstraints,
        storage_scheme: MerchantStorageScheme,
        limit: i64,
        offset: i64,
    ) -> CustomResult<Vec<diesel_refund::Refund>, errors::StorageError> {
        self.diesel_store
            .filter_refund_by_constraints(
                merchant_id,
                refund_details,
                storage_scheme,
                limit,
                offset,
            )
            .await
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn filter_refund_by_meta_constraints(
        &self,
        merchant_id: &id_type::MerchantId,
        refund_details: &common_utils::types::TimeRange,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<api_models::refunds::RefundListMetaData, errors::StorageError> {
        self.diesel_store
            .filter_refund_by_meta_constraints(merchant_id, refund_details, storage_scheme)
            .await
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn get_refund_status_with_count(
        &self,
        merchant_id: &id_type::MerchantId,
        profile_id_list: Option<Vec<id_type::ProfileId>>,
        constraints: &common_utils::types::TimeRange,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<(common_enums::RefundStatus, i64)>, errors::StorageError> {
        self.diesel_store
            .get_refund_status_with_count(merchant_id, profile_id_list, constraints, storage_scheme)
            .await
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    async fn get_total_count_of_refunds(
        &self,
        merchant_id: &id_type::MerchantId,
        refund_details: &refunds::RefundListConstraints,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<i64, errors::StorageError> {
        self.diesel_store
            .get_total_count_of_refunds(merchant_id, refund_details, storage_scheme)
            .await
    }

    #[cfg(feature = "v2")]
    async fn get_total_count_of_refunds(
        &self,
        merchant_id: &id_type::MerchantId,
        refund_details: refunds::RefundListConstraints,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<i64, errors::StorageError> {
        self.diesel_store
            .get_total_count_of_refunds(merchant_id, refund_details, storage_scheme)
            .await
    }
}

#[async_trait::async_trait]
impl MerchantKeyStoreInterface for KafkaStore {
    async fn insert_merchant_key_store(
        &self,
        state: &KeyManagerState,
        merchant_key_store: domain::MerchantKeyStore,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::MerchantKeyStore, errors::StorageError> {
        self.diesel_store
            .insert_merchant_key_store(state, merchant_key_store, key)
            .await
    }

    async fn get_merchant_key_store_by_merchant_id(
        &self,
        state: &KeyManagerState,
        merchant_id: &id_type::MerchantId,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<domain::MerchantKeyStore, errors::StorageError> {
        self.diesel_store
            .get_merchant_key_store_by_merchant_id(state, merchant_id, key)
            .await
    }

    async fn delete_merchant_key_store_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        self.diesel_store
            .delete_merchant_key_store_by_merchant_id(merchant_id)
            .await
    }

    #[cfg(feature = "olap")]
    async fn list_multiple_key_stores(
        &self,
        state: &KeyManagerState,
        merchant_ids: Vec<id_type::MerchantId>,
        key: &Secret<Vec<u8>>,
    ) -> CustomResult<Vec<domain::MerchantKeyStore>, errors::StorageError> {
        self.diesel_store
            .list_multiple_key_stores(state, merchant_ids, key)
            .await
    }
    async fn get_all_key_stores(
        &self,
        state: &KeyManagerState,
        key: &Secret<Vec<u8>>,
        from: u32,
        to: u32,
    ) -> CustomResult<Vec<domain::MerchantKeyStore>, errors::StorageError> {
        self.diesel_store
            .get_all_key_stores(state, key, from, to)
            .await
    }
}

#[async_trait::async_trait]
impl ProfileInterface for KafkaStore {
    async fn insert_business_profile(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        business_profile: domain::Profile,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        self.diesel_store
            .insert_business_profile(key_manager_state, merchant_key_store, business_profile)
            .await
    }

    async fn find_business_profile_by_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_id: &id_type::ProfileId,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        self.diesel_store
            .find_business_profile_by_profile_id(key_manager_state, merchant_key_store, profile_id)
            .await
    }

    async fn find_business_profile_by_merchant_id_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        merchant_id: &id_type::MerchantId,
        profile_id: &id_type::ProfileId,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        self.diesel_store
            .find_business_profile_by_merchant_id_profile_id(
                key_manager_state,
                merchant_key_store,
                merchant_id,
                profile_id,
            )
            .await
    }

    async fn update_profile_by_profile_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        current_state: domain::Profile,
        business_profile_update: domain::ProfileUpdate,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        self.diesel_store
            .update_profile_by_profile_id(
                key_manager_state,
                merchant_key_store,
                current_state,
                business_profile_update,
            )
            .await
    }

    async fn delete_profile_by_profile_id_merchant_id(
        &self,
        profile_id: &id_type::ProfileId,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<bool, errors::StorageError> {
        self.diesel_store
            .delete_profile_by_profile_id_merchant_id(profile_id, merchant_id)
            .await
    }

    async fn list_profile_by_merchant_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<Vec<domain::Profile>, errors::StorageError> {
        self.diesel_store
            .list_profile_by_merchant_id(key_manager_state, merchant_key_store, merchant_id)
            .await
    }

    async fn find_business_profile_by_profile_name_merchant_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        profile_name: &str,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<domain::Profile, errors::StorageError> {
        self.diesel_store
            .find_business_profile_by_profile_name_merchant_id(
                key_manager_state,
                merchant_key_store,
                profile_name,
                merchant_id,
            )
            .await
    }
}

#[async_trait::async_trait]
impl ReverseLookupInterface for KafkaStore {
    async fn insert_reverse_lookup(
        &self,
        new: ReverseLookupNew,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<ReverseLookup, errors::StorageError> {
        self.diesel_store
            .insert_reverse_lookup(new, storage_scheme)
            .await
    }

    async fn get_lookup_by_lookup_id(
        &self,
        id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<ReverseLookup, errors::StorageError> {
        self.diesel_store
            .get_lookup_by_lookup_id(id, storage_scheme)
            .await
    }
}

#[async_trait::async_trait]
impl RoutingAlgorithmInterface for KafkaStore {
    async fn insert_routing_algorithm(
        &self,
        routing_algorithm: storage::RoutingAlgorithm,
    ) -> CustomResult<storage::RoutingAlgorithm, errors::StorageError> {
        self.diesel_store
            .insert_routing_algorithm(routing_algorithm)
            .await
    }

    async fn find_routing_algorithm_by_profile_id_algorithm_id(
        &self,
        profile_id: &id_type::ProfileId,
        algorithm_id: &id_type::RoutingId,
    ) -> CustomResult<storage::RoutingAlgorithm, errors::StorageError> {
        self.diesel_store
            .find_routing_algorithm_by_profile_id_algorithm_id(profile_id, algorithm_id)
            .await
    }

    async fn find_routing_algorithm_by_algorithm_id_merchant_id(
        &self,
        algorithm_id: &id_type::RoutingId,
        merchant_id: &id_type::MerchantId,
    ) -> CustomResult<storage::RoutingAlgorithm, errors::StorageError> {
        self.diesel_store
            .find_routing_algorithm_by_algorithm_id_merchant_id(algorithm_id, merchant_id)
            .await
    }

    async fn find_routing_algorithm_metadata_by_algorithm_id_profile_id(
        &self,
        algorithm_id: &id_type::RoutingId,
        profile_id: &id_type::ProfileId,
    ) -> CustomResult<storage::RoutingProfileMetadata, errors::StorageError> {
        self.diesel_store
            .find_routing_algorithm_metadata_by_algorithm_id_profile_id(algorithm_id, profile_id)
            .await
    }

    async fn list_routing_algorithm_metadata_by_profile_id(
        &self,
        profile_id: &id_type::ProfileId,
        limit: i64,
        offset: i64,
    ) -> CustomResult<Vec<storage::RoutingProfileMetadata>, errors::StorageError> {
        self.diesel_store
            .list_routing_algorithm_metadata_by_profile_id(profile_id, limit, offset)
            .await
    }

    async fn list_routing_algorithm_metadata_by_merchant_id(
        &self,
        merchant_id: &id_type::MerchantId,
        limit: i64,
        offset: i64,
    ) -> CustomResult<Vec<storage::RoutingProfileMetadata>, errors::StorageError> {
        self.diesel_store
            .list_routing_algorithm_metadata_by_merchant_id(merchant_id, limit, offset)
            .await
    }

    async fn list_routing_algorithm_metadata_by_merchant_id_transaction_type(
        &self,
        merchant_id: &id_type::MerchantId,
        transaction_type: &enums::TransactionType,
        limit: i64,
        offset: i64,
    ) -> CustomResult<Vec<storage::RoutingProfileMetadata>, errors::StorageError> {
        self.diesel_store
            .list_routing_algorithm_metadata_by_merchant_id_transaction_type(
                merchant_id,
                transaction_type,
                limit,
                offset,
            )
            .await
    }
}

#[async_trait::async_trait]
impl GsmInterface for KafkaStore {
    async fn add_gsm_rule(
        &self,
        rule: hyperswitch_domain_models::gsm::GatewayStatusMap,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
        self.diesel_store.add_gsm_rule(rule).await
    }

    async fn find_gsm_decision(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> CustomResult<String, errors::StorageError> {
        self.diesel_store
            .find_gsm_decision(connector, flow, sub_flow, code, message)
            .await
    }

    async fn find_gsm_rule(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
        self.diesel_store
            .find_gsm_rule(connector, flow, sub_flow, code, message)
            .await
    }

    async fn update_gsm_rule(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
        data: hyperswitch_domain_models::gsm::GatewayStatusMappingUpdate,
    ) -> CustomResult<hyperswitch_domain_models::gsm::GatewayStatusMap, errors::StorageError> {
        self.diesel_store
            .update_gsm_rule(connector, flow, sub_flow, code, message, data)
            .await
    }

    async fn delete_gsm_rule(
        &self,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> CustomResult<bool, errors::StorageError> {
        self.diesel_store
            .delete_gsm_rule(connector, flow, sub_flow, code, message)
            .await
    }
}

#[async_trait::async_trait]
impl UnifiedTranslationsInterface for KafkaStore {
    async fn add_unfied_translation(


pub fn get_role_id(&self) -> &str {
        &self.role_id
    }


impl super::behaviour::Conversion for MerchantKeyStore {
    type DstType = diesel_models::merchant_key_store::MerchantKeyStore;
    type NewDstType = diesel_models::merchant_key_store::MerchantKeyStoreNew;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(diesel_models::merchant_key_store::MerchantKeyStore {
            key: self.key.into(),
            merchant_id: self.merchant_id,
            created_at: self.created_at,
        })
    }

    async fn convert_back(
        state: &KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        _key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        let identifier = keymanager::Identifier::Merchant(item.merchant_id.clone());

        Ok(Self {
            key: crypto_operation(
                state,
                type_name!(Self::DstType),
                CryptoOperation::Decrypt(item.key),
                identifier,
                key.peek(),
            )
            .await
            .and_then(|val| val.try_into_operation())
            .change_context(ValidationError::InvalidValue {
                message: "Failed while decrypting customer data".to_string(),
            })?,
            merchant_id: item.merchant_id,
            created_at: item.created_at,
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        Ok(diesel_models::merchant_key_store::MerchantKeyStoreNew {
            merchant_id: self.merchant_id,
            key: self.key.into(),
            created_at: date_time::now(),
        })
    }
}

impl super::behaviour::Conversion for for MerchantKeyStore


pub struct SantanderPixQRPaymentRequest {
    #[serde(rename = "calendario")]
    pub calender: SantanderCalendar,
    #[serde(rename = "devedor")]
    pub debtor: Option<SantanderDebtor>,
    #[serde(rename = "valor")]
    pub value: SantanderValue,
    #[serde(rename = "chave")]
    pub key: Secret<String>,
    #[serde(rename = "solicitacaoPagador")]
    pub request_payer: Option<String>,
    #[serde(rename = "infoAdicionais")]
    pub additional_info: Option<Vec<SantanderAdditionalInfo>>,
}


impl api::Refund for Sift {}