File: crates/router/tests/connectors/main.rs

#![allow(
    clippy::expect_used,
    clippy::panic,
    clippy::unwrap_in_result,
    clippy::unwrap_used
)]
use test_utils::connector_auth;

mod aci;
mod adyen;
mod adyenplatform;
mod affirm;
mod airwallex;
mod amazonpay;
mod authorizedotnet;
mod bambora;
mod bamboraapac;
#[cfg(feature = "dummy_connector")]
mod bankofamerica;
mod barclaycard;
#[cfg(feature = "dummy_connector")]
mod billwerk;
mod bitpay;
mod blackhawknetwork;
mod bluecode;
mod bluesnap;
mod boku;
mod breadpay;
mod cashtocode;
mod celero;
mod chargebee;
mod checkbook;
mod checkout;
mod coinbase;
mod cryptopay;
mod cybersource;
mod datatrans;
mod deutschebank;
mod dlocal;
#[cfg(feature = "dummy_connector")]
mod dummyconnector;
mod dwolla;
mod ebanx;
mod elavon;
mod facilitapay;
mod fiserv;
mod fiservemea;
mod fiuu;
mod flexiti;
mod forte;
mod getnet;
mod globalpay;
mod globepay;
mod gocardless;
mod gpayments;
mod helcim;
mod hipay;
mod hyperswitch_vault;
mod hyperwallet;
mod iatapay;
mod inespay;
mod itaubank;
mod jpmorgan;
mod juspaythreedsserver;
mod katapult;
mod mifinity;
mod mollie;
mod moneris;
mod mpgs;
mod multisafepay;
mod netcetera;
mod nexinets;
mod nexixpay;
mod nmi;
mod nomupay;
mod noon;
mod nordea;
mod novalnet;
mod nuvei;
#[cfg(feature = "dummy_connector")]
mod opayo;
mod opennode;
mod paybox;
#[cfg(feature = "dummy_connector")]
mod payeezy;
mod payload;
mod payme;
mod payone;
mod paypal;
mod paysafe;
mod paystack;
mod paytm;
mod payu;
mod phonepe;
mod placetopay;
mod plaid;
mod powertranz;
#[cfg(feature = "dummy_connector")]
mod prophetpay;
mod rapyd;
mod razorpay;
mod redsys;
mod santander;
mod shift4;
mod sift;
mod silverflow;
mod square;
mod stax;
mod stripe;
mod stripebilling;
mod taxjar;
mod tokenio;
mod trustpay;
mod trustpayments;
mod tsys;
mod unified_authentication_service;
mod utils;
mod vgs;
mod volt;
mod wellsfargo;
mod worldpayvantiv;
// mod wellsfargopayout;
#[cfg(feature = "payouts")]
mod wise;
mod worldline;
mod worldpay;
mod worldpayxml;
mod zen;
mod zsl;


impl MandateSetup for Plaid {}


impl api::PaymentSync for Breadpay {}


pub async fn execute_connector_processing_step<'b, T, Req, Resp>(
    state: &'b SessionState,
    connector_integration: BoxedConnectorIntegration<'_, T, Req, Resp>,
    req: &'b PaymentAuthRouterData<T, Req, Resp>,
    connector: &pm_auth_types::PaymentMethodAuthConnectors,
) -> errors::CustomResult<PaymentAuthRouterData<T, Req, Resp>, ConnectorError>
where
    T: Clone + 'static,
    Req: Clone + 'static,
    Resp: Clone + 'static,
{
    let mut router_data = req.clone();

    let connector_request = connector_integration.build_request(req, connector)?;

    match connector_request {
        Some(request) => {
            logger::debug!(connector_request=?request);
            let response = services::api::call_connector_api(
                state,
                request,
                "execute_connector_processing_step",
            )
            .await;
            logger::debug!(connector_response=?response);
            match response {
                Ok(body) => {
                    let response = match body {
                        Ok(body) => {
                            let body = pm_auth_types::Response {
                                headers: body.headers,
                                response: body.response,
                                status_code: body.status_code,
                            };
                            let connector_http_status_code = Some(body.status_code);
                            let mut data =
                                connector_integration.handle_response(&router_data, body)?;
                            data.connector_http_status_code = connector_http_status_code;

                            data
                        }
                        Err(body) => {
                            let body = pm_auth_types::Response {
                                headers: body.headers,
                                response: body.response,
                                status_code: body.status_code,
                            };
                            router_data.connector_http_status_code = Some(body.status_code);

                            let error = match body.status_code {
                                500..=511 => connector_integration.get_5xx_error_response(body)?,
                                _ => connector_integration.get_error_response(body)?,
                            };

                            router_data.response = Err(error);

                            router_data
                        }
                    };
                    Ok(response)
                }
                Err(error) => {
                    if error.current_context().is_upstream_timeout() {
                        let error_response = pm_auth_types::ErrorResponse {
                            code: consts::REQUEST_TIMEOUT_ERROR_CODE.to_string(),
                            message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(),
                            reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()),
                            status_code: 504,
                        };
                        router_data.response = Err(error_response);
                        router_data.connector_http_status_code = Some(504);
                        Ok(router_data)
                    } else {
                        Err(error.change_context(ConnectorError::ProcessingStepFailed(None)))
                    }
                }
            }
        }
        None => Ok(router_data),
    }
}


pub async fn call_create_connector_customer_if_required<F, Req, D>(
    state: &SessionState,
    customer: &Option<domain::Customer>,
    merchant_context: &domain::MerchantContext,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    payment_data: &mut D,
) -> RouterResult<Option<storage::CustomerUpdate>>
where
    F: Send + Clone + Sync,
    Req: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, Req, router_types::PaymentsResponseData>,
    RouterData<F, Req, router_types::PaymentsResponseData>: Feature<F, Req> + Send,

    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, Req, router_types::PaymentsResponseData>,
{
    let connector_name = payment_data.get_payment_attempt().connector.clone();

    match connector_name {
        Some(connector_name) => {
            let connector = api::ConnectorData::get_connector_by_name(
                &state.conf.connectors,
                &connector_name,
                api::GetToken::Connector,
                merchant_connector_account.get_id(),
            )?;

            let (should_call_connector, existing_connector_customer_id) =
                customers::should_call_connector_create_customer(
                    state,
                    &connector,
                    customer,
                    merchant_connector_account,
                );

            if should_call_connector {
                // Create customer at connector and update the customer table to store this data
                let router_data = payment_data
                    .construct_router_data(
                        state,
                        connector.connector.id(),
                        merchant_context,
                        customer,
                        merchant_connector_account,
                        None,
                        None,
                    )
                    .await?;

                let connector_customer_id = router_data
                    .create_connector_customer(state, &connector)
                    .await?;

                let customer_update = customers::update_connector_customer_in_customers(
                    merchant_connector_account,
                    customer.as_ref(),
                    connector_customer_id.clone(),
                )
                .await;

                payment_data.set_connector_customer_id(connector_customer_id);
                Ok(customer_update)
            } else {
                // Customer already created in previous calls use the same value, no need to update
                payment_data.set_connector_customer_id(
                    existing_connector_customer_id.map(ToOwned::to_owned),
                );
                Ok(None)
            }
        }
        None => Ok(None),
    }
}


impl api::PaymentVoid for Celero {}


pub struct NexixpayTransactionResponse {
    order_id: String,
    operation_id: String,
    operation_result: NexixpayPaymentStatus,
    operation_type: NexixpayOperationType,
}


pub struct PreAuthenticationData {
    pub threeds_server_transaction_id: String,
    pub message_version: common_utils::types::SemanticVersion,
    pub acquirer_bin: Option<String>,
    pub acquirer_merchant_id: Option<String>,
    pub acquirer_country_code: Option<String>,
    pub connector_metadata: Option<serde_json::Value>,
}


pub async fn delete_by_user_id_tenant_id_org_id_merchant_id_profile_id(
        conn: &PgPooledConn,
        user_id: String,
        tenant_id: id_type::TenantId,
        org_id: id_type::OrganizationId,
        merchant_id: id_type::MerchantId,
        profile_id: id_type::ProfileId,
        version: UserRoleVersion,
    ) -> StorageResult<Self> {
        let check_lineage = Self::check_user_in_lineage(
            tenant_id,
            Some(org_id),
            Some(merchant_id),
            Some(profile_id),
        );

        let predicate = dsl::user_id
            .eq(user_id)
            .and(check_lineage)
            .and(dsl::version.eq(version));

        generics::generic_delete_one_with_result::<<Self as HasTable>::Table, _, _>(conn, predicate)
            .await
    }


OpenAPI Block Path: components.schemas.DisputeStage
{
  "type": "string",
  "description": "Stage of the dispute",
  "enum": [
    "pre_dispute",
    "dispute",
    "pre_arbitration",
    "arbitration",
    "dispute_reversal"
  ]
}


File: crates/router/src/types/storage/merchant_connector_account.rs

pub use diesel_models::merchant_connector_account::{
    MerchantConnectorAccount, MerchantConnectorAccountNew, MerchantConnectorAccountUpdateInternal,
};

pub use crate::types::domain::MerchantConnectorAccountUpdate;


OpenAPI Block Path: components.schemas.MerchantCategoryCode
{
  "type": "string",
  "enum": [
    "5411",
    "7011",
    "0763",
    "8111",
    "5021",
    "4816",
    "5661"
  ]
}


File: crates/router/src/core/verification.rs
 Public functions: 2

pub mod utils;
use api_models::verifications::{self, ApplepayMerchantResponse};
use common_utils::{errors::CustomResult, request::RequestContent};
use error_stack::ResultExt;
use masking::ExposeInterface;

use crate::{core::errors, headers, logger, routes::SessionState, services};

const APPLEPAY_INTERNAL_MERCHANT_NAME: &str = "Applepay_merchant";

pub async fn verify_merchant_creds_for_applepay(
    state: SessionState,
    body: verifications::ApplepayMerchantVerificationRequest,
    merchant_id: common_utils::id_type::MerchantId,
    profile_id: Option<common_utils::id_type::ProfileId>,
) -> CustomResult<services::ApplicationResponse<ApplepayMerchantResponse>, errors::ApiErrorResponse>
{
    let applepay_merchant_configs = state.conf.applepay_merchant_configs.get_inner();

    let applepay_internal_merchant_identifier = applepay_merchant_configs
        .common_merchant_identifier
        .clone()
        .expose();
    let cert_data = applepay_merchant_configs.merchant_cert.clone();
    let key_data = applepay_merchant_configs.merchant_cert_key.clone();
    let applepay_endpoint = &applepay_merchant_configs.applepay_endpoint;

    let request_body = verifications::ApplepayMerchantVerificationConfigs {
        domain_names: body.domain_names.clone(),
        encrypt_to: applepay_internal_merchant_identifier.clone(),
        partner_internal_merchant_identifier: applepay_internal_merchant_identifier,
        partner_merchant_name: APPLEPAY_INTERNAL_MERCHANT_NAME.to_string(),
    };

    let apple_pay_merch_verification_req = services::RequestBuilder::new()
        .method(services::Method::Post)
        .url(applepay_endpoint)
        .attach_default_headers()
        .headers(vec![(
            headers::CONTENT_TYPE.to_string(),
            "application/json".to_string().into(),
        )])
        .set_body(RequestContent::Json(Box::new(request_body)))
        .add_certificate(Some(cert_data))
        .add_certificate_key(Some(key_data))
        .build();

    let response = services::call_connector_api(
        &state,
        apple_pay_merch_verification_req,
        "verify_merchant_creds_for_applepay",
    )
    .await;
    utils::log_applepay_verification_response_if_error(&response);

    let applepay_response =
        response.change_context(errors::ApiErrorResponse::InternalServerError)?;

    // Error is already logged
    match applepay_response {
        Ok(_) => {
            utils::check_existence_and_add_domain_to_db(
                &state,
                merchant_id,
                profile_id,
                body.merchant_connector_account_id.clone(),
                body.domain_names.clone(),
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)?;
            Ok(services::api::ApplicationResponse::Json(
                ApplepayMerchantResponse {
                    status_message: "Applepay verification Completed".to_string(),
                },
            ))
        }
        Err(error) => {
            logger::error!(?error);
            Err(errors::ApiErrorResponse::InvalidRequestData {
                message: "Applepay verification Failed".to_string(),
            }
            .into())
        }
    }
}

pub async fn get_verified_apple_domains_with_mid_mca_id(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId,
) -> CustomResult<
    services::ApplicationResponse<verifications::ApplepayVerifiedDomainsResponse>,
    errors::ApiErrorResponse,
> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let key_store = db
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &merchant_id,
            &db.get_master_key().to_vec().into(),
        )
        .await
        .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    #[cfg(feature = "v1")]
    let verified_domains = db
        .find_by_merchant_connector_account_merchant_id_merchant_connector_id(
            key_manager_state,
            &merchant_id,
            &merchant_connector_id,
            &key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?
        .applepay_verified_domains
        .unwrap_or_default();

    #[cfg(feature = "v2")]
    let verified_domains = db
        .find_merchant_connector_account_by_id(
            key_manager_state,
            &merchant_connector_id,
            &key_store,
        )
        .await
        .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?
        .applepay_verified_domains
        .unwrap_or_default();

    Ok(services::api::ApplicationResponse::Json(
        verifications::ApplepayVerifiedDomainsResponse { verified_domains },
    ))
}


pub async fn routing_unlink_config() {}


impl UnifiedTranslationsInterface for KafkaStore {
    async fn add_unfied_translation(
        &self,
        translation: storage::UnifiedTranslationsNew,
    ) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
        self.diesel_store.add_unfied_translation(translation).await
    }

    async fn find_translation(
        &self,
        unified_code: String,
        unified_message: String,
        locale: String,
    ) -> CustomResult<String, errors::StorageError> {
        self.diesel_store
            .find_translation(unified_code, unified_message, locale)
            .await
    }

    async fn update_translation(
        &self,
        unified_code: String,
        unified_message: String,
        locale: String,
        data: storage::UnifiedTranslationsUpdate,
    ) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
        self.diesel_store
            .update_translation(unified_code, unified_message, locale, data)
            .await
    }

    async fn delete_translation(
        &self,
        unified_code: String,
        unified_message: String,
        locale: String,
    ) -> CustomResult<bool, errors::StorageError> {
        self.diesel_store
            .delete_translation(unified_code, unified_message, locale)
            .await
    }
}

impl UnifiedTranslationsInterface for for KafkaStore


File: crates/common_enums/src/enums/ui.rs

use std::fmt;

use serde::{de::Visitor, Deserialize, Deserializer, Serialize};
use utoipa::ToSchema;

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    Serialize,
    Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "lowercase")]
pub enum ElementPosition {
    Left,
    #[default]
    #[serde(rename = "top left")]
    TopLeft,
    Top,
    #[serde(rename = "top right")]
    TopRight,
    Right,
    #[serde(rename = "bottom right")]
    BottomRight,
    Bottom,
    #[serde(rename = "bottom left")]
    BottomLeft,
    Center,
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, strum::Display, strum::EnumString, ToSchema)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
pub enum ElementSize {
    Variants(SizeVariants),
    Percentage(u32),
    Pixels(u32),
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    Serialize,
    Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum SizeVariants {
    #[default]
    Cover,
    Contain,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    Serialize,
    Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum PaymentLinkDetailsLayout {
    #[default]
    Layout1,
    Layout2,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    Serialize,
    Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum PaymentLinkSdkLabelType {
    #[default]
    Above,
    Floating,
    Never,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    Serialize,
    Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum PaymentLinkShowSdkTerms {
    Always,
    #[default]
    Auto,
    Never,
}

impl<'de> Deserialize<'de> for ElementSize {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct ElementSizeVisitor;

        impl Visitor<'_> for ElementSizeVisitor {
            type Value = ElementSize;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("a string with possible values - contain, cover or values in percentage or pixels. For eg: 48px or 50%")
            }

            fn visit_str<E>(self, value: &str) -> Result<ElementSize, E>
            where
                E: serde::de::Error,
            {
                if let Some(percent) = value.strip_suffix('%') {
                    percent
                        .parse::<u32>()
                        .map(ElementSize::Percentage)
                        .map_err(E::custom)
                } else if let Some(px) = value.strip_suffix("px") {
                    px.parse::<u32>()
                        .map(ElementSize::Pixels)
                        .map_err(E::custom)
                } else {
                    match value {
                        "cover" => Ok(ElementSize::Variants(SizeVariants::Cover)),
                        "contain" => Ok(ElementSize::Variants(SizeVariants::Contain)),
                        _ => Err(E::custom("invalid size variant")),
                    }
                }
            }
        }

        deserializer.deserialize_str(ElementSizeVisitor)
    }
}

impl Serialize for ElementSize {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::ser::Serializer,
    {
        match self {
            Self::Variants(variant) => serializer.serialize_str(variant.to_string().as_str()),
            Self::Pixels(pixel_count) => {
                serializer.serialize_str(format!("{pixel_count}px").as_str())
            }
            Self::Percentage(pixel_count) => {
                serializer.serialize_str(format!("{pixel_count}%").as_str())
            }
        }
    }
}


pub async fn disable_decision_engine_dynamic_routing_setup(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    dynamic_routing_type: routing_types::DynamicRoutingType,
    dynamic_routing_algo_ref: &mut routing_types::DynamicRoutingAlgorithmRef,
) -> RouterResult<()> {
    logger::debug!(
        "performing call with open_router for profile {}",
        profile_id.get_string_repr()
    );

    let decision_engine_request = open_router::FetchRoutingConfig {
        merchant_id: profile_id.get_string_repr().to_string(),
        algorithm: match dynamic_routing_type {
            routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
                open_router::AlgorithmType::SuccessRate
            }
            routing_types::DynamicRoutingType::EliminationRouting => {
                open_router::AlgorithmType::Elimination
            }
            routing_types::DynamicRoutingType::ContractBasedRouting => {
                return Err((errors::ApiErrorResponse::InvalidRequestData {
                    message: "Contract routing is not enabled for decision engine".to_string(),
                })
                .into())
            }
        },
    };

    // Create merchant in Decision Engine if it is not already created
    create_merchant_in_decision_engine_if_not_exists(state, profile_id, dynamic_routing_algo_ref)
        .await;

    routing_utils::ConfigApiClient::send_decision_engine_request::<_, serde_json::Value>(
        state,
        services::Method::Post,
        DECISION_ENGINE_RULE_DELETE_ENDPOINT,
        Some(decision_engine_request),
        None,
        None,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Unable to disable decision engine dynamic routing")?;

    Ok(())
}


File: crates/router/src/routes/dummy_connector/core.rs
 Public functions: 6

use app::SessionState;
use common_utils::generate_id_with_default_len;
use error_stack::ResultExt;

use super::{errors, types, utils};
use crate::{
    routes::{app, dummy_connector::consts},
    services::api,
    utils::OptionExt,
};

#[cfg(feature = "dummy_connector")]
pub async fn payment(
    state: SessionState,
    req: types::DummyConnectorPaymentRequest,
) -> types::DummyConnectorResponse<types::DummyConnectorPaymentResponse> {
    utils::tokio_mock_sleep(
        state.conf.dummy_connector.payment_duration,
        state.conf.dummy_connector.payment_tolerance,
    )
    .await;

    let payment_attempt: types::DummyConnectorPaymentAttempt = req.into();
    let payment_data =
        types::DummyConnectorPaymentData::process_payment_attempt(&state, payment_attempt)?;

    utils::store_data_in_redis(
        &state,
        payment_data.attempt_id.clone(),
        payment_data.payment_id.clone(),
        state.conf.dummy_connector.authorize_ttl,
    )
    .await?;
    utils::store_data_in_redis(
        &state,
        payment_data.payment_id.get_string_repr().to_owned(),
        payment_data.clone(),
        state.conf.dummy_connector.payment_ttl,
    )
    .await?;
    Ok(api::ApplicationResponse::Json(payment_data.into()))
}

pub async fn payment_data(
    state: SessionState,
    req: types::DummyConnectorPaymentRetrieveRequest,
) -> types::DummyConnectorResponse<types::DummyConnectorPaymentResponse> {
    utils::tokio_mock_sleep(
        state.conf.dummy_connector.payment_retrieve_duration,
        state.conf.dummy_connector.payment_retrieve_tolerance,
    )
    .await;

    let payment_data = utils::get_payment_data_from_payment_id(&state, req.payment_id).await?;
    Ok(api::ApplicationResponse::Json(payment_data.into()))
}

#[cfg(all(feature = "dummy_connector", feature = "v1"))]
pub async fn payment_authorize(
    state: SessionState,
    req: types::DummyConnectorPaymentConfirmRequest,
) -> types::DummyConnectorResponse<String> {
    let payment_data = utils::get_payment_data_by_attempt_id(&state, req.attempt_id.clone()).await;
    let dummy_connector_conf = &state.conf.dummy_connector;

    if let Ok(payment_data_inner) = payment_data {
        let return_url = format!(
            "{}/dummy-connector/complete/{}",
            state.base_url, req.attempt_id
        );
        Ok(api::ApplicationResponse::FileData((
            utils::get_authorize_page(payment_data_inner, return_url, dummy_connector_conf)
                .as_bytes()
                .to_vec(),
            mime::TEXT_HTML,
        )))
    } else {
        Ok(api::ApplicationResponse::FileData((
            utils::get_expired_page(dummy_connector_conf)
                .as_bytes()
                .to_vec(),
            mime::TEXT_HTML,
        )))
    }
}

#[cfg(all(feature = "dummy_connector", feature = "v1"))]
pub async fn payment_complete(
    state: SessionState,
    req: types::DummyConnectorPaymentCompleteRequest,
) -> types::DummyConnectorResponse<()> {
    utils::tokio_mock_sleep(
        state.conf.dummy_connector.payment_duration,
        state.conf.dummy_connector.payment_tolerance,
    )
    .await;

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

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

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

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

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

#[cfg(all(feature = "dummy_connector", feature = "v1"))]
pub async fn refund_payment(
    state: SessionState,
    req: types::DummyConnectorRefundRequest,
) -> types::DummyConnectorResponse<types::DummyConnectorRefundResponse> {
    utils::tokio_mock_sleep(
        state.conf.dummy_connector.refund_duration,
        state.conf.dummy_connector.refund_tolerance,
    )
    .await;

    let payment_id = req
        .payment_id
        .get_required_value("payment_id")
        .change_context(errors::DummyConnectorErrors::MissingRequiredField {
            field_name: "payment_id",
        })?;

    let mut payment_data =
        utils::get_payment_data_from_payment_id(&state, payment_id.get_string_repr().to_owned())
            .await?;

    payment_data.is_eligible_for_refund(req.amount)?;

    let refund_id = generate_id_with_default_len(consts::REFUND_ID_PREFIX);
    payment_data.eligible_amount -= req.amount;

    utils::store_data_in_redis(
        &state,
        payment_id.get_string_repr().to_owned(),
        payment_data.to_owned(),
        state.conf.dummy_connector.payment_ttl,
    )
    .await?;

    let refund_data = types::DummyConnectorRefundResponse::new(
        types::DummyConnectorStatus::Succeeded,
        refund_id.to_owned(),
        payment_data.currency,
        common_utils::date_time::now(),
        payment_data.amount,
        req.amount,
    );

    utils::store_data_in_redis(
        &state,
        refund_id,
        refund_data.to_owned(),
        state.conf.dummy_connector.refund_ttl,
    )
    .await?;
    Ok(api::ApplicationResponse::Json(refund_data))
}

#[cfg(all(feature = "dummy_connector", feature = "v1"))]
pub async fn refund_data(
    state: SessionState,
    req: types::DummyConnectorRefundRetrieveRequest,
) -> types::DummyConnectorResponse<types::DummyConnectorRefundResponse> {
    let refund_id = req.refund_id;
    utils::tokio_mock_sleep(
        state.conf.dummy_connector.refund_retrieve_duration,
        state.conf.dummy_connector.refund_retrieve_tolerance,
    )
    .await;

    let redis_conn = state
        .store
        .get_redis_conn()
        .change_context(errors::DummyConnectorErrors::InternalServerError)
        .attach_printable("Failed to get redis connection")?;
    let refund_data = redis_conn
        .get_and_deserialize_key::<types::DummyConnectorRefundResponse>(
            &refund_id.as_str().into(),
            "DummyConnectorRefundResponse",
        )
        .await
        .change_context(errors::DummyConnectorErrors::RefundNotFound)?;
    Ok(api::ApplicationResponse::Json(refund_data))
}


/// Generate a test payment id with prefix test_
    pub fn generate_test_payment_id_for_sample_data() -> Self {
        let id = generate_id_with_default_len("test");
        let alphanumeric_id = AlphaNumericId::new_unchecked(id);
        let id = LengthId::new_unchecked(alphanumeric_id);
        Self(id)
    }


pub struct ChatRequest {
    pub message: Secret<String>,
}


pub async fn update(self, conn: &PgPooledConn, dispute: DisputeUpdate) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::dispute_id.eq(self.dispute_id.to_owned()),
            DisputeUpdateInternal::from(dispute),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }


impl api::Payment for Tokenio {}


impl SeriesBucket for Granularity {
    type SeriesType = time::PrimitiveDateTime;

    type GranularityLevel = TimeGranularityLevel;

    fn get_lowest_common_granularity_level(&self) -> Self::GranularityLevel {
        match self {
            Self::OneMin => TimeGranularityLevel::Minute,
            Self::FiveMin | Self::FifteenMin | Self::ThirtyMin | Self::OneHour => {
                TimeGranularityLevel::Hour
            }
            Self::OneDay => TimeGranularityLevel::Day,
        }
    }

    fn get_bucket_size(&self) -> u8 {
        match self {
            Self::OneMin => 60,
            Self::FiveMin => 5,
            Self::FifteenMin => 15,
            Self::ThirtyMin => 30,
            Self::OneHour => 60,
            Self::OneDay => 24,
        }
    }

    fn clip_to_start(
        &self,
        value: Self::SeriesType,
    ) -> error_stack::Result<Self::SeriesType, PostProcessingError> {
        let clip_start = |value: u8, modulo: u8| -> u8 { value - value % modulo };

        let clipped_time = match (
            self.get_lowest_common_granularity_level(),
            self.get_bucket_size(),
        ) {
            (TimeGranularityLevel::Minute, i) => time::Time::MIDNIGHT
                .replace_second(clip_start(value.second(), i))
                .and_then(|t| t.replace_minute(value.minute()))
                .and_then(|t| t.replace_hour(value.hour())),
            (TimeGranularityLevel::Hour, i) => time::Time::MIDNIGHT
                .replace_minute(clip_start(value.minute(), i))
                .and_then(|t| t.replace_hour(value.hour())),
            (TimeGranularityLevel::Day, i) => {
                time::Time::MIDNIGHT.replace_hour(clip_start(value.hour(), i))
            }
        }
        .change_context(PostProcessingError::BucketClipping)?;

        Ok(value.replace_time(clipped_time))
    }

    fn clip_to_end(
        &self,
        value: Self::SeriesType,
    ) -> error_stack::Result<Self::SeriesType, PostProcessingError> {
        let clip_end = |value: u8, modulo: u8| -> u8 { value + modulo - 1 - value % modulo };

        let clipped_time = match (
            self.get_lowest_common_granularity_level(),
            self.get_bucket_size(),
        ) {
            (TimeGranularityLevel::Minute, i) => time::Time::MIDNIGHT
                .replace_second(clip_end(value.second(), i))
                .and_then(|t| t.replace_minute(value.minute()))
                .and_then(|t| t.replace_hour(value.hour())),
            (TimeGranularityLevel::Hour, i) => time::Time::MIDNIGHT
                .replace_minute(clip_end(value.minute(), i))
                .and_then(|t| t.replace_hour(value.hour())),
            (TimeGranularityLevel::Day, i) => {
                time::Time::MIDNIGHT.replace_hour(clip_end(value.hour(), i))
            }
        }
        .change_context(PostProcessingError::BucketClipping)
        .attach_printable_lazy(|| format!("Bucket Clip Error: {value}"))?;

        Ok(value.replace_time(clipped_time))
    }
}


pub fn get_json_body(
        self,
    ) -> common_utils::errors::CustomResult<R, common_utils::errors::ValidationError> {
        match self {
            Self::Json(body) | Self::JsonWithHeaders((body, _)) => Ok(body),
            Self::TextPlain(_)
            | Self::JsonForRedirection(_)
            | Self::Form(_)
            | Self::PaymentLinkForm(_)
            | Self::FileData(_)
            | Self::GenericLinkForm(_)
            | Self::StatusOk => Err(common_utils::errors::ValidationError::InvalidValue {
                message: "expected either Json or JsonWithHeaders Response".to_string(),
            }
            .into()),
        }
    }


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),
    }
}


pub trait UpdateTracker<F, D, Req>: Send {
    /// Update the tracker information with the new data from request or calculated by the operations performed after get trackers
    /// This will persist the SessionData ( PaymentData ) in the database
    ///
    /// In case we are calling a processor / connector, we persist all the data in the database and then call the connector
    async fn update_trackers<'b>(
        &'b self,
        db: &'b SessionState,
        req_state: ReqState,
        payment_data: D,
        customer: Option<domain::Customer>,
        storage_scheme: enums::MerchantStorageScheme,
        updated_customer: Option<storage::CustomerUpdate>,
        mechant_key_store: &domain::MerchantKeyStore,
        frm_suggestion: Option<FrmSuggestion>,
        header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(BoxedOperation<'b, F, Req, D>, D)>
    where
        F: 'b + Send;
}


pub struct Card {
    pub brand_reference: Option<Secret<String>>,
}


pub struct FiuuWebhooksPaymentResponse {
    pub skey: Secret<String>,
    pub status: FiuuPaymentWebhookStatus,
    #[serde(rename = "orderid")]
    pub order_id: String,
    #[serde(rename = "tranID")]
    pub tran_id: String,
    pub nbcb: String,
    pub amount: StringMajorUnit,
    pub currency: String,
    pub domain: Secret<String>,
    pub appcode: Option<Secret<String>>,
    pub paydate: String,
    pub channel: String,
    pub error_desc: Option<String>,
    pub error_code: Option<String>,
    #[serde(rename = "extraP")]
    pub extra_parameters: Option<Secret<String>>,
}


pub struct TrustpaymentsErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
    pub network_advice_code: Option<String>,
    pub network_decline_code: Option<String>,
    pub network_error_message: Option<String>,
}


File: crates/hyperswitch_domain_models/src/router_flow_types/files.rs
 Public structs: 2

#[derive(Debug, Clone)]
pub struct Retrieve;

#[derive(Debug, Clone)]
pub struct Upload;


/// get_skip_saving_wallet_at_connector_key
    pub fn get_skip_saving_wallet_at_connector_key(&self) -> String {
        format!("skip_saving_wallet_at_connector_{}", self.get_string_repr())
    }


pub struct MerchantConnectorUpdate {
    /// Type of the Connector for the financial use case. Could range from Payments to Accounting to Banking.
    #[schema(value_type = ConnectorType, example = "payment_processor")]
    pub connector_type: api_enums::ConnectorType,

    /// This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports. Eg: if your profile label is `default`, connector label can be `stripe_default`
    #[schema(example = "stripe_US_travel")]
    pub connector_label: Option<String>,

    /// An object containing the required details/credentials for a Connector account.
    #[schema(value_type = Option<MerchantConnectorDetails>,example = json!({ "auth_type": "HeaderKey","api_key": "Basic MyVerySecretApiKey" }))]
    pub connector_account_details: Option<pii::SecretSerdeValue>,

    /// An object containing the details about the payment methods that need to be enabled under this merchant connector account
    #[schema(example = json!([
        {
            "payment_method": "wallet",
            "payment_method_types": [
                "upi_collect",
                "upi_intent"
            ],
            "payment_method_issuers": [
                "labore magna ipsum",
                "aute"
            ],
            "payment_schemes": [
                "Discover",
                "Discover"
            ],
            "accepted_currencies": {
                "type": "enable_only",
                "list": ["USD", "EUR"]
            },
            "accepted_countries": {
                "type": "disable_only",
                "list": ["FR", "DE","IN"]
            },
            "minimum_amount": 1,
            "maximum_amount": 68607706,
            "recurring_enabled": true,
            "installment_payment_enabled": true
        }
    ]))]
    pub payment_methods_enabled: Option<Vec<PaymentMethodsEnabled>>,

    /// Webhook details of this merchant connector
    #[schema(example = json!({
        "connector_webhook_details": {
            "merchant_secret": "1234567890987654321"
        }
    }))]
    pub connector_webhook_details: Option<MerchantConnectorWebhookDetails>,

    /// Metadata is useful for storing additional, unstructured information on an object.
    #[schema(value_type = Option<Object>,max_length = 255,example = json!({ "city": "NY", "unit": "245" }))]
    pub metadata: Option<pii::SecretSerdeValue>,

    /// A boolean value to indicate if the connector is in Test mode. By default, its value is false.
    #[schema(default = false, example = false)]
    pub test_mode: Option<bool>,

    /// A boolean value to indicate if the connector is disabled. By default, its value is false.
    #[schema(default = false, example = false)]
    pub disabled: Option<bool>,

    /// Contains the frm configs for the merchant connector
    #[schema(example = json!(consts::FRM_CONFIGS_EG))]
    pub frm_configs: Option<Vec<FrmConfigs>>,

    /// pm_auth_config will relate MCA records to their respective chosen auth services, based on payment_method and pmt
    #[schema(value_type = Option<Object>)]
    pub pm_auth_config: Option<pii::SecretSerdeValue>,

    #[schema(value_type = ConnectorStatus, example = "inactive")]
    pub status: Option<api_enums::ConnectorStatus>,

    /// In case the merchant needs to store any additional sensitive data
    #[schema(value_type = Option<AdditionalMerchantData>)]
    pub additional_merchant_data: Option<AdditionalMerchantData>,

    /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials
    #[schema(value_type = Option<ConnectorWalletDetails>)]
    pub connector_wallets_details: Option<ConnectorWalletDetails>,
}


impl Dispute {
    pub async fn find_by_merchant_id_payment_id_connector_dispute_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
        connector_dispute_id: &str,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned()))
                .and(dsl::connector_dispute_id.eq(connector_dispute_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_dispute_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        dispute_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::dispute_id.eq(dispute_id.to_owned())),
        )
        .await
    }

    pub async fn find_by_merchant_id_payment_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<
            <Self as HasTable>::Table,
            _,
            <<Self as HasTable>::Table as Table>::PrimaryKey,
            _,
        >(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::payment_id.eq(payment_id.to_owned())),
            None,
            None,
            None,
        )
        .await
    }

    pub async fn update(self, conn: &PgPooledConn, dispute: DisputeUpdate) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::dispute_id.eq(self.dispute_id.to_owned()),
            DisputeUpdateInternal::from(dispute),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }
}


pub struct DlocalRefundsSyncRequest {
    pub refund_id: String,
}


impl ConnectorValidation for Coingate {}

impl ConnectorValidation for for Coingate


pub fn get_revenue_recovery_metadata(
        &self,
    ) -> Option<diesel_models::types::PaymentRevenueRecoveryMetadata> {
        self.feature_metadata
            .as_ref()
            .and_then(|feature_metadata| feature_metadata.payment_revenue_recovery_metadata.clone())
    }


pub struct GlobalPayPayer {
    /// Unique identifier for the Payer on the Global Payments system.
    #[serde(rename = "id", skip_serializing_if = "Option::is_none")]
    pub payer_id: Option<String>,
}


pub trait ApiEventMetricAnalytics: LoadRow<ApiEventMetricRow> + LoadRow<LatencyAvg> {}


pub async fn find_by_profile_id_connector_reference_id(
        conn: &PgPooledConn,
        profile_id: &common_utils::id_type::ProfileId,
        connector_reference_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::profile_id
                .eq(profile_id.to_owned())
                .and(dsl::connector_reference_id.eq(connector_reference_id.to_owned())),
        )
        .await
    }


pub struct AcquirerDetails {
    pub acquirer_bin: String,
    pub acquirer_merchant_mid: String,
    pub acquirer_country_code: Option<String>,
}


pub struct PaypalBatchResponse {
    pub payout_batch_id: String,
    pub batch_status: PaypalFulfillStatus,
}


pub struct RSyncEdgeData {
    node: RSyncNodeData,
}


impl ConnectorValidation for Razorpay {
    //TODO: implement functions when support enabled
}

impl ConnectorValidation for for Razorpay


File: crates/router/src/events/outgoing_webhook_logs.rs
 Public functions: 1
 Public structs: 1

use api_models::{enums::EventType as OutgoingWebhookEventType, webhooks::OutgoingWebhookContent};
use common_enums::WebhookDeliveryAttempt;
use serde::Serialize;
use serde_json::Value;
use time::OffsetDateTime;

use super::EventType;
use crate::services::kafka::KafkaMessage;

#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct OutgoingWebhookEvent {
    tenant_id: common_utils::id_type::TenantId,
    merchant_id: common_utils::id_type::MerchantId,
    event_id: String,
    event_type: OutgoingWebhookEventType,
    #[serde(flatten)]
    content: Option<OutgoingWebhookEventContent>,
    is_error: bool,
    error: Option<Value>,
    created_at_timestamp: i128,
    initial_attempt_id: Option<String>,
    status_code: Option<u16>,
    delivery_attempt: Option<WebhookDeliveryAttempt>,
}

#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(tag = "outgoing_webhook_event_type", rename_all = "snake_case")]
pub enum OutgoingWebhookEventContent {
    #[cfg(feature = "v1")]
    Payment {
        payment_id: common_utils::id_type::PaymentId,
        content: Value,
    },
    #[cfg(feature = "v2")]
    Payment {
        payment_id: common_utils::id_type::GlobalPaymentId,
        content: Value,
    },
    Payout {
        payout_id: common_utils::id_type::PayoutId,
        content: Value,
    },
    #[cfg(feature = "v1")]
    Refund {
        payment_id: common_utils::id_type::PaymentId,
        refund_id: String,
        content: Value,
    },
    #[cfg(feature = "v2")]
    Refund {
        payment_id: common_utils::id_type::GlobalPaymentId,
        refund_id: common_utils::id_type::GlobalRefundId,
        content: Value,
    },
    #[cfg(feature = "v1")]
    Dispute {
        payment_id: common_utils::id_type::PaymentId,
        attempt_id: String,
        dispute_id: String,
        content: Value,
    },
    #[cfg(feature = "v2")]
    Dispute {
        payment_id: common_utils::id_type::GlobalPaymentId,
        attempt_id: String,
        dispute_id: String,
        content: Value,
    },
    Mandate {
        payment_method_id: String,
        mandate_id: String,
        content: Value,
    },
}
pub trait OutgoingWebhookEventMetric {
    fn get_outgoing_webhook_event_content(&self) -> Option<OutgoingWebhookEventContent>;
}

#[cfg(feature = "v1")]
impl OutgoingWebhookEventMetric for OutgoingWebhookContent {
    fn get_outgoing_webhook_event_content(&self) -> Option<OutgoingWebhookEventContent> {
        match self {
            Self::PaymentDetails(payment_payload) => Some(OutgoingWebhookEventContent::Payment {
                payment_id: payment_payload.payment_id.clone(),
                content: masking::masked_serialize(&payment_payload)
                    .unwrap_or(serde_json::json!({"error":"failed to serialize"})),
            }),
            Self::RefundDetails(refund_payload) => Some(OutgoingWebhookEventContent::Refund {
                payment_id: refund_payload.payment_id.clone(),
                refund_id: refund_payload.get_refund_id_as_string(),
                content: masking::masked_serialize(&refund_payload)
                    .unwrap_or(serde_json::json!({"error":"failed to serialize"})),
            }),
            Self::DisputeDetails(dispute_payload) => Some(OutgoingWebhookEventContent::Dispute {
                payment_id: dispute_payload.payment_id.clone(),
                attempt_id: dispute_payload.attempt_id.clone(),
                dispute_id: dispute_payload.dispute_id.clone(),
                content: masking::masked_serialize(&dispute_payload)
                    .unwrap_or(serde_json::json!({"error":"failed to serialize"})),
            }),
            Self::MandateDetails(mandate_payload) => Some(OutgoingWebhookEventContent::Mandate {
                payment_method_id: mandate_payload.payment_method_id.clone(),
                mandate_id: mandate_payload.mandate_id.clone(),
                content: masking::masked_serialize(&mandate_payload)
                    .unwrap_or(serde_json::json!({"error":"failed to serialize"})),
            }),
            #[cfg(feature = "payouts")]
            Self::PayoutDetails(payout_payload) => Some(OutgoingWebhookEventContent::Payout {
                payout_id: payout_payload.payout_id.clone(),
                content: masking::masked_serialize(&payout_payload)
                    .unwrap_or(serde_json::json!({"error":"failed to serialize"})),
            }),
        }
    }
}

#[cfg(feature = "v2")]
impl OutgoingWebhookEventMetric for OutgoingWebhookContent {
    fn get_outgoing_webhook_event_content(&self) -> Option<OutgoingWebhookEventContent> {
        match self {
            Self::PaymentDetails(payment_payload) => Some(OutgoingWebhookEventContent::Payment {
                payment_id: payment_payload.id.clone(),
                content: masking::masked_serialize(&payment_payload)
                    .unwrap_or(serde_json::json!({"error":"failed to serialize"})),
            }),
            Self::RefundDetails(refund_payload) => Some(OutgoingWebhookEventContent::Refund {
                payment_id: refund_payload.payment_id.clone(),
                refund_id: refund_payload.id.clone(),
                content: masking::masked_serialize(&refund_payload)
                    .unwrap_or(serde_json::json!({"error":"failed to serialize"})),
            }),
            Self::DisputeDetails(dispute_payload) => {
                //TODO: add support for dispute outgoing webhook
                todo!()
            }
            Self::MandateDetails(mandate_payload) => Some(OutgoingWebhookEventContent::Mandate {
                payment_method_id: mandate_payload.payment_method_id.clone(),
                mandate_id: mandate_payload.mandate_id.clone(),
                content: masking::masked_serialize(&mandate_payload)
                    .unwrap_or(serde_json::json!({"error":"failed to serialize"})),
            }),
            #[cfg(feature = "payouts")]
            Self::PayoutDetails(payout_payload) => Some(OutgoingWebhookEventContent::Payout {
                payout_id: payout_payload.payout_id.clone(),
                content: masking::masked_serialize(&payout_payload)
                    .unwrap_or(serde_json::json!({"error":"failed to serialize"})),
            }),
        }
    }
}

impl OutgoingWebhookEvent {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        tenant_id: common_utils::id_type::TenantId,
        merchant_id: common_utils::id_type::MerchantId,
        event_id: String,
        event_type: OutgoingWebhookEventType,
        content: Option<OutgoingWebhookEventContent>,
        error: Option<Value>,
        initial_attempt_id: Option<String>,
        status_code: Option<u16>,
        delivery_attempt: Option<WebhookDeliveryAttempt>,
    ) -> Self {
        Self {
            tenant_id,
            merchant_id,
            event_id,
            event_type,
            content,
            is_error: error.is_some(),
            error,
            created_at_timestamp: OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000_000,
            initial_attempt_id,
            status_code,
            delivery_attempt,
        }
    }
}

impl KafkaMessage for OutgoingWebhookEvent {
    fn event_type(&self) -> EventType {
        EventType::OutgoingWebhookLogs
    }

    fn key(&self) -> String {
        self.event_id.clone()
    }
}


pub struct CeleroPaymentsResponse {
    pub status: CeleroResponseStatus,
    pub msg: String,
    pub data: Option<CeleroTransactionResponseData>,
}


impl Responder {
    let flow = Flow::CustomersDelete;
    let id = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        id,
        |state, auth: auth::AuthenticationData, id, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            delete_customer(state, merchant_context, id)
        },
        auth::auth_type(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::JWTAuth {
                permission: Permission::MerchantCustomerWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


/// Allows [error_stack::Report] to change between error contexts
/// using the dependent [ErrorSwitch] trait to define relations & mappings between traits
pub trait ReportSwitchExt<T, U> {
    /// Switch to the intended report by calling switch
    /// requires error switch to be already implemented on the error type
    fn switch(self) -> Result<T, error_stack::Report<U>>;
}


pub struct Message {
    pub code: String,
    pub description: String,
}


pub struct ForteRefundRequest {
    action: String,
    authorization_amount: FloatMajorUnit,
    original_transaction_id: String,
    authorization_code: String,
}


pub async fn config_key_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<api_types::Config>,
) -> impl Responder {
    let flow = Flow::CreateConfigKey;
    let payload = json_payload.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, data, _| configs::set_config(state, data),
        &ADMIN_API_AUTH,
        api_locking::LockAction::NotApplicable,
    )
    .await
}


pub trait ApiEventFilterAnalytics: LoadRow<ApiEventFilter> {}


pub fn generate_digest(&self, payload: &[u8]) -> String {
        let payload_digest = digest::digest(&digest::SHA256, payload);
        consts::BASE64_ENGINE.encode(payload_digest)
    }


OpenAPI Block Path: components.schemas.PayoutsCreateRequest
{
  "type": "object",
  "required": [
    "amount",
    "currency"
  ],
  "properties": {
    "merchant_order_reference_id": {
      "type": "string",
      "description": "Your unique identifier for this payout or order. This ID helps you reconcile payouts on your system. If provided, it is passed to the connector if supported.",
      "example": "merchant_order_ref_123",
      "nullable": true,
      "maxLength": 255
    },
    "amount": {
      "type": "integer",
      "format": "int64",
      "description": "The payout amount. Amount for the payout in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,",
      "minimum": 0
    },
    "currency": {
      "$ref": "#/components/schemas/Currency"
    },
    "routing": {
      "allOf": [
        {
          "$ref": "#/components/schemas/StaticRoutingAlgorithm"
        }
      ],
      "nullable": true
    },
    "connector": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/PayoutConnectors"
      },
      "description": "This field allows the merchant to manually select a connector with which the payout can go through.",
      "example": [
        "wise",
        "adyen"
      ],
      "nullable": true
    },
    "confirm": {
      "type": "boolean",
      "description": "This field is used when merchant wants to confirm the payout, thus useful for the payout _Confirm_ request. Ideally merchants should _Create_ a payout, _Update_ it (if required), then _Confirm_ it.",
      "default": false,
      "example": true,
      "nullable": true
    },
    "payout_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PayoutType"
        }
      ],
      "nullable": true
    },
    "payout_method_data": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PayoutMethodData"
        }
      ],
      "nullable": true
    },
    "billing": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Address"
        }
      ],
      "nullable": true
    },
    "auto_fulfill": {
      "type": "boolean",
      "description": "Set to true to confirm the payout without review, no further action required",
      "default": false,
      "example": true,
      "nullable": true
    },
    "customer_id": {
      "type": "string",
      "description": "The identifier for the customer object. If not provided the customer ID will be autogenerated. _Deprecated: Use customer_id instead._",
      "deprecated": true,
      "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44",
      "nullable": true,
      "maxLength": 255
    },
    "customer": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CustomerDetails"
        }
      ],
      "nullable": true
    },
    "return_url": {
      "type": "string",
      "description": "The URL to redirect after the completion of the operation",
      "example": "https://hyperswitch.io",
      "nullable": true
    },
    "business_country": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CountryAlpha2"
        }
      ],
      "nullable": true
    },
    "business_label": {
      "type": "string",
      "description": "Business label of the merchant for this payout. _Deprecated: Use profile_id instead._",
      "deprecated": true,
      "example": "food",
      "nullable": true
    },
    "description": {
      "type": "string",
      "description": "A description of the payout",
      "example": "It's my first payout request",
      "nullable": true
    },
    "entity_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PayoutEntityType"
        }
      ],
      "nullable": true
    },
    "recurring": {
      "type": "boolean",
      "description": "Specifies whether or not the payout request is recurring",
      "default": false,
      "nullable": true
    },
    "metadata": {
      "type": "object",
      "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.",
      "nullable": true
    },
    "payout_token": {
      "type": "string",
      "description": "Provide a reference to a stored payout method, used to process the payout.",
      "example": "187282ab-40ef-47a9-9206-5099ba31e432",
      "nullable": true
    },
    "profile_id": {
      "type": "string",
      "description": "The business profile to use for this payout, especially if there are multiple business profiles associated with the account, otherwise default business profile associated with the merchant account will be used.",
      "nullable": true
    },
    "priority": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PayoutSendPriority"
        }
      ],
      "nullable": true
    },
    "payout_link": {
      "type": "boolean",
      "description": "Whether to get the payout link (if applicable). Merchant need to specify this during the Payout _Create_, this field can not be updated during Payout _Update_.",
      "default": false,
      "example": true,
      "nullable": true
    },
    "payout_link_config": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PayoutCreatePayoutLinkConfig"
        }
      ],
      "nullable": true
    },
    "session_expiry": {
      "type": "integer",
      "format": "int32",
      "description": "Will be used to expire client secret after certain amount of time to be supplied in seconds\n(900) for 15 mins",
      "example": 900,
      "nullable": true,
      "minimum": 0
    },
    "email": {
      "type": "string",
      "description": "Customer's email. _Deprecated: Use customer object instead._",
      "deprecated": true,
      "example": "johntest@test.com",
      "nullable": true,
      "maxLength": 255
    },
    "name": {
      "type": "string",
      "description": "Customer's name. _Deprecated: Use customer object instead._",
      "deprecated": true,
      "example": "John Test",
      "nullable": true,
      "maxLength": 255
    },
    "phone": {
      "type": "string",
      "description": "Customer's phone. _Deprecated: Use customer object instead._",
      "deprecated": true,
      "example": "9123456789",
      "nullable": true,
      "maxLength": 255
    },
    "phone_country_code": {
      "type": "string",
      "description": "Customer's phone country code. _Deprecated: Use customer object instead._",
      "deprecated": true,
      "example": "+1",
      "nullable": true,
      "maxLength": 255
    },
    "payout_method_id": {
      "type": "string",
      "description": "Identifier for payout method",
      "nullable": true
    }
  }
}


pub async fn create_vault_token_core(
    state: SessionState,
    merchant_account: &domain::MerchantAccount,
    merchant_key_store: &domain::MerchantKeyStore,
    req: api_models::tokenization::GenericTokenizationRequest,
) -> RouterResponse<api_models::tokenization::GenericTokenizationResponse> {
    // Generate a unique vault ID
    let vault_id = domain::VaultId::generate(uuid::Uuid::now_v7().to_string());
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let customer_id = req.customer_id.clone();
    // Create vault request
    let payload = pm_types::AddVaultRequest {
        entity_id: req.customer_id.to_owned(),
        vault_id: vault_id.clone(),
        data: req.token_request.clone(),
        ttl: state.conf.locker.ttl_for_storage_in_secs,
    }
    .encode_to_vec()
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to encode Request")?;

    // Call the vault service
    let resp = pm_vault::call_to_vault::<pm_types::AddVault>(&state, payload.clone())
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Call to vault failed")?;

    // Parse the response
    let stored_resp: pm_types::AddVaultResponse = resp
        .parse_struct("AddVaultResponse")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to parse data into AddVaultResponse")?;

    // Create new tokenization record
    let tokenization_new = hyperswitch_domain_models::tokenization::Tokenization {
        id: id_type::GlobalTokenId::generate(&state.conf.cell_information.id),
        merchant_id: merchant_account.get_id().clone(),
        customer_id: customer_id.clone(),
        locker_id: stored_resp.vault_id.get_string_repr().to_string(),
        created_at: common_utils::date_time::now(),
        updated_at: common_utils::date_time::now(),
        flag: enums::TokenizationFlag::Enabled,
        version: enums::ApiVersion::V2,
    };

    // Insert into database
    let tokenization = db
        .insert_tokenization(
            tokenization_new,
            &(merchant_key_store.clone()),
            key_manager_state,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to insert tokenization record")?;

    // Convert to TokenizationResponse
    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        api_models::tokenization::GenericTokenizationResponse {
            id: tokenization.id,
            created_at: tokenization.created_at,
            flag: tokenization.flag,
        },
    ))
}


pub struct PowertranzPaymentsRequest {
    transaction_identifier: String,
    total_amount: f64,
    currency_code: String,
    three_d_secure: bool,
    source: Source,
    order_identifier: String,
    // billing and shipping are optional fields and requires state in iso codes, hence commenting it
    // can be added later if we have iso code for state
    // billing_address: Option<PowertranzAddressDetails>,
    // shipping_address: Option<PowertranzAddressDetails>,
    extended_data: Option<ExtendedData>,
}


File: crates/router/src/types/api/payouts_v2.rs

pub use api_models::payouts::{
    AchBankTransfer, BacsBankTransfer, Bank as BankPayout, CardPayout, PayoutActionRequest,
    PayoutAttemptResponse, PayoutCreateRequest, PayoutCreateResponse, PayoutListConstraints,
    PayoutListFilterConstraints, PayoutListFilters, PayoutListResponse, PayoutMethodData,
    PayoutRequest, PayoutRetrieveBody, PayoutRetrieveRequest, PixBankTransfer, SepaBankTransfer,
    Wallet as WalletPayout,
};
pub use hyperswitch_domain_models::router_flow_types::payouts::{
    PoCancel, PoCreate, PoEligibility, PoFulfill, PoQuote, PoRecipient, PoRecipientAccount, PoSync,
};
pub use hyperswitch_interfaces::api::payouts_v2::{
    PayoutCancelV2, PayoutCreateV2, PayoutEligibilityV2, PayoutFulfillV2, PayoutQuoteV2,
    PayoutRecipientAccountV2, PayoutRecipientV2, PayoutSyncV2,
};

use crate::types::api as api_types;

pub trait PayoutsV2:
    api_types::ConnectorCommon
    + PayoutCancelV2
    + PayoutCreateV2
    + PayoutEligibilityV2
    + PayoutFulfillV2
    + PayoutQuoteV2
    + PayoutRecipientV2
    + PayoutSyncV2
    + PayoutRecipientAccountV2
{
}


pub fn diesel_enum_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
    let tokens =
        macros::diesel_enum_derive_inner(&ast).unwrap_or_else(|error| error.to_compile_error());
    tokens.into()
}


impl Responder {
        let flow = AnalyticsFlow::GenerateRefundReport;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move {
                let user = UserInterface::find_user_by_id(&*state.global_store, &user_id)
                    .await
                    .change_context(AnalyticsError::UnknownError)?;

                let user_email = UserEmail::from_pii_email(user.email)
                    .change_context(AnalyticsError::UnknownError)?
                    .get_secret();

                let org_id = auth.merchant_account.get_org_id();
                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: None,
                    auth: AuthInfo::OrgLevel {
                        org_id: org_id.clone(),
                    },
                    email: user_email,
                };

                let json_bytes =
                    serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?;
                invoke_lambda(
                    &state.conf.report_download_config.refund_function,
                    &state.conf.report_download_config.region,
                    &json_bytes,
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::OrganizationReportRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


impl VaultingInterface for VaultRetrieve {
    fn get_vaulting_request_url() -> &'static str {
        consts::VAULT_RETRIEVE_REQUEST_URL
    }

    fn get_vaulting_flow_name() -> &'static str {
        consts::VAULT_RETRIEVE_FLOW_TYPE
    }
}

impl VaultingInterface for for VaultRetrieve


pub struct RefundListConstraints {
    pub payment_id: Option<common_utils::id_type::GlobalPaymentId>,
    pub refund_id: Option<common_utils::id_type::GlobalRefundId>,
    pub profile_id: common_utils::id_type::ProfileId,
    pub limit: Option<i64>,
    pub offset: Option<i64>,
    pub time_range: Option<common_utils::types::TimeRange>,
    pub amount_filter: Option<api_models::payments::AmountFilter>,
    pub connector: Option<Vec<String>>,
    pub connector_id_list: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
    pub currency: Option<Vec<common_enums::Currency>>,
    pub refund_status: Option<Vec<common_enums::RefundStatus>>,
}


pub struct CoinbaseConnectorMeta {
    pub pricing_type: String,
}


pub struct DisputeMetricsAccumulator {
    pub disputes_status_rate: RateAccumulator,
    pub disputed_amount: DisputedAmountAccumulator,
    pub dispute_lost_amount: DisputedAmountAccumulator,
}


OpenAPI Block Path: components.schemas.DocumentKind
{
  "type": "string",
  "enum": [
    "CNPJ",
    "CPF"
  ]
}


pub struct PayoutListFilterConstraints {
    /// The identifier for payout
    #[schema(
    value_type = Option<String>,
    min_length = 30,
    max_length = 30,
    example = "187282ab-40ef-47a9-9206-5099ba31e432"
)]
    pub payout_id: Option<id_type::PayoutId>,
    /// The merchant order reference ID for payout
    #[schema(value_type = Option<String>, max_length = 255, example = "merchant_order_ref_123")]
    pub merchant_order_reference_id: Option<String>,
    /// The identifier for business profile
    #[schema(value_type = Option<String>)]
    pub profile_id: Option<id_type::ProfileId>,
    /// The identifier for customer
    #[schema(value_type = Option<String>,example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: Option<id_type::CustomerId>,
    /// The limit on the number of objects. The default limit is 10 and max limit is 20
    #[serde(default = "default_payouts_list_limit")]
    pub limit: u32,
    /// The starting point within a list of objects
    pub offset: Option<u32>,
    /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc).
    #[serde(flatten)]
    #[schema(value_type = Option<TimeRange>)]
    pub time_range: Option<common_utils::types::TimeRange>,
    /// The list of connectors to filter payouts list
    #[schema(value_type = Option<Vec<PayoutConnectors>>, max_length = 255, example = json!(["wise", "adyen"]))]
    pub connector: Option<Vec<api_enums::PayoutConnectors>>,
    /// The list of currencies to filter payouts list
    #[schema(value_type = Currency, example = "USD")]
    pub currency: Option<Vec<api_enums::Currency>>,
    /// The list of payout status to filter payouts list
    #[schema(value_type = Option<Vec<PayoutStatus>>, example = json!(["pending", "failed"]))]
    pub status: Option<Vec<api_enums::PayoutStatus>>,
    /// The list of payout methods to filter payouts list
    #[schema(value_type = Option<Vec<PayoutType>>, example = json!(["bank", "card"]))]
    pub payout_method: Option<Vec<common_enums::PayoutType>>,
    /// Type of recipient
    #[schema(value_type = PayoutEntityType, example = "Individual")]
    pub entity_type: Option<common_enums::PayoutEntityType>,
}


impl ApiEventMetric for BlocklistRequest {}

impl ApiEventMetric for for BlocklistRequest


pub struct TwoFactorAuthAttempts {
    pub is_completed: bool,
    pub remaining_attempts: u8,
}


pub async fn list_invitable_roles_at_entity_level(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<role_api::ListRolesAtEntityLevelRequest>,
) -> HttpResponse {
    let flow = Flow::ListInvitableRolesAtEntityLevel;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        query.into_inner(),
        |state, user_from_token, req, _| {
            role_core::list_roles_at_entity_level(
                state,
                user_from_token,
                req,
                role_api::RoleCheckType::Invite,
            )
        },
        &auth::JWTAuth {
            permission: Permission::ProfileUserRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct Cardholder {
    /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same.
    ///
    /// Accepted values:
    /// - `Y` -> Shipping Address matches Billing Address
    /// - `N` -> Shipping Address does not match Billing Address
    ///
    /// If the field is not set and the shipping and billing addresses are the same, the 3DS Server will set the value to
    /// `Y`. Otherwise, the value will not be changed.
    ///
    /// This field is optional.
    addr_match: Option<String>,

    /// The city of the Cardholder billing address associated with the card used for this purchase.
    ///
    /// This field is limited to a maximum of 50 characters.
    ///
    /// This field is required unless market or regional mandate restricts sending this information.
    bill_addr_city: Option<String>,

    /// The country of the Cardholder billing address associated with the card used for this purchase.
    ///
    /// This field is limited to 3 characters. This value shall be the ISO 3166-1 numeric country code, except values
    /// from range 901 - 999 which are reserved by ISO.
    ///
    /// The field is required if Cardholder Billing Address State is present and unless market or regional mandate
    /// restricts sending this information.
    bill_addr_country: Option<String>,

    /// First line of the street address or equivalent local portion of the Cardholder billing address associated with
    /// the card use for this purchase.
    ///
    /// This field is limited to a maximum of 50 characters.
    ///
    /// This field is required unless market or regional mandate restricts sending this information.
    bill_addr_line1: Option<masking::Secret<String>>,

    /// Second line of the street address or equivalent local portion of the Cardholder billing address associated with
    /// the card use for this purchase.
    ///
    /// This field is limited to a maximum of 50 characters.
    ///
    /// This field is required unless market or regional mandate restricts sending this information.
    bill_addr_line2: Option<masking::Secret<String>>,

    /// Third line of the street address or equivalent local portion of the Cardholder billing address associated with
    /// the card use for this purchase.
    ///
    /// This field is limited to a maximum of 50 characters.
    ///
    /// This field is required unless market or regional mandate restricts sending this information.
    bill_addr_line3: Option<masking::Secret<String>>,

    /// ZIP or other postal code of the Cardholder billing address associated with the card used for this purchase.
    ///
    /// This field is limited to a maximum of 16 characters.
    ///
    /// This field is required unless market or regional mandate restricts sending this information.
    bill_addr_post_code: Option<masking::Secret<String>>,

    /// The state or province of the Cardholder billing address associated with the card used for this purchase.
    ///
    /// This field is limited to 3 characters. The value should be the country subdivision code defined in ISO 3166-2.
    ///
    /// This field is required unless State is not applicable for this country and unless market or regional mandate
    /// restricts sending this information.
    bill_addr_state: Option<masking::Secret<String>>,

    /// The email address associated with the account that is either entered by the Cardholder, or is on file with
    /// the 3DS Requestor.
    ///
    /// This field is limited to a maximum of 256 characters and shall meet requirements of Section 3.4 of
    /// IETF RFC 5322.
    ///
    /// This field is required unless market or regional mandate restricts sending this information.
    email: Option<Email>,

    /// The home phone provided by the Cardholder.
    ///
    /// Refer to ITU-E.164 for additional information on format and length.
    ///
    /// This field is required if available, unless market or regional mandate restricts sending this information.
    home_phone: Option<PhoneNumber>,

    /// The mobile phone provided by the Cardholder.
    ///
    /// Refer to ITU-E.164 for additional information on format and length.
    ///
    /// This field is required if available, unless market or regional mandate restricts sending this information.
    mobile_phone: Option<PhoneNumber>,

    /// The work phone provided by the Cardholder.
    ///
    /// Refer to ITU-E.164 for additional information on format and length.
    ///
    /// This field is required if available, unless market or regional mandate restricts sending this information.
    work_phone: Option<PhoneNumber>,

    /// Name of the Cardholder.
    ///
    /// This field is limited to 2-45 characters.
    ///
    /// This field is required unless market or regional mandate restricts sending this information.
    ///
    /// Starting from EMV 3DS 2.3.1:
    /// This field is limited to 1-45 characters.
    cardholder_name: Option<masking::Secret<String>>,

    /// City portion of the shipping address requested by the Cardholder.
    ///
    /// This field is required unless shipping information is the same as billing information, or market or regional
    /// mandate restricts sending this information.
    ship_addr_city: Option<String>,

    /// Country of the shipping address requested by the Cardholder.
    ///
    /// This field is limited to 3 characters. This value shall be the ISO 3166-1 numeric country code, except values
    /// from range 901 - 999 which are reserved by ISO.
    ///
    /// This field is required if Cardholder Shipping Address State is present and if shipping information are not the same
    /// as billing information. This field can be omitted if market or regional mandate restricts sending this information.
    ship_addr_country: Option<String>,

    /// First line of the street address or equivalent local portion of the shipping address associated with
    /// the card use for this purchase.
    ///
    /// This field is limited to a maximum of 50 characters.
    ///
    /// This field is required unless shipping information is the same as billing information, or market or regional
    /// mandate restricts sending this information.
    ship_addr_line1: Option<masking::Secret<String>>,

    /// Second line of the street address or equivalent local portion of the shipping address associated with
    /// the card use for this purchase.
    ///
    /// This field is limited to a maximum of 50 characters.
    ///
    /// This field is required unless shipping information is the same as billing information, or market or regional
    /// mandate restricts sending this information.
    ship_addr_line2: Option<masking::Secret<String>>,

    /// Third line of the street address or equivalent local portion of the shipping address associated with
    /// the card use for this purchase.
    ///
    /// This field is limited to a maximum of 50 characters.
    ///
    /// This field is required unless shipping information is the same as billing information, or market or regional
    /// mandate restricts sending this information.
    ship_addr_line3: Option<masking::Secret<String>>,

    /// ZIP or other postal code of the shipping address associated with the card used for this purchase.
    ///
    /// This field is limited to a maximum of 16 characters.
    ///
    /// This field is required unless shipping information is the same as billing information, or market or regional
    /// mandate restricts sending this information.
    ship_addr_post_code: Option<masking::Secret<String>>,

    /// The state or province of the shipping address associated with the card used for this purchase.
    ///
    /// This field is limited to 3 characters. The value should be the country subdivision code defined in ISO 3166-2.
    ///
    /// This field is required unless shipping information is the same as billing information, or State is not applicable
    /// for this country, or market or regional mandate restricts sending this information.
    ship_addr_state: Option<masking::Secret<String>>,

    /// Tax ID is the Cardholder's tax identification.
    ///
    /// The value is limited to 45 characters.
    ///
    /// This field is required depending on the rules provided by the Directory Server.
    /// Available for supporting EMV 3DS 2.3.1 and later versions.
    tax_id: Option<String>,
}


Documentation: api-reference/v2/connector-account/connector-account--delete.mdx
# Type: Doc File

---
openapi: delete /v2/connector-accounts/{id}
---


File: crates/currency_conversion/src/conversion.rs
 Public functions: 1

use common_enums::Currency;
use rust_decimal::Decimal;
use rusty_money::Money;

use crate::{
    error::CurrencyConversionError,
    types::{currency_match, ExchangeRates},
};

pub fn convert(
    ex_rates: &ExchangeRates,
    from_currency: Currency,
    to_currency: Currency,
    amount: i64,
) -> Result<Decimal, CurrencyConversionError> {
    let money_minor = Money::from_minor(amount, currency_match(from_currency));
    let base_currency = ex_rates.base_currency;
    if to_currency == base_currency {
        ex_rates.forward_conversion(*money_minor.amount(), from_currency)
    } else if from_currency == base_currency {
        ex_rates.backward_conversion(*money_minor.amount(), to_currency)
    } else {
        let base_conversion_amt =
            ex_rates.forward_conversion(*money_minor.amount(), from_currency)?;
        ex_rates.backward_conversion(base_conversion_amt, to_currency)
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::expect_used, clippy::print_stdout)]
    use std::collections::HashMap;

    use crate::types::CurrencyFactors;
    #[test]
    fn currency_to_currency_conversion() {
        use super::*;
        let mut conversion: HashMap<Currency, CurrencyFactors> = HashMap::new();
        let inr_conversion_rates =
            CurrencyFactors::new(Decimal::new(823173, 4), Decimal::new(1214, 5));
        let szl_conversion_rates =
            CurrencyFactors::new(Decimal::new(194423, 4), Decimal::new(514, 4));
        let convert_from = Currency::SZL;
        let convert_to = Currency::INR;
        let amount = 2000;
        let base_currency = Currency::USD;
        conversion.insert(convert_from, inr_conversion_rates);
        conversion.insert(convert_to, szl_conversion_rates);
        let sample_rate = ExchangeRates::new(base_currency, conversion);
        let res =
            convert(&sample_rate, convert_from, convert_to, amount).expect("converted_currency");
        println!("The conversion from {amount} {convert_from} to {convert_to} is {res:?}");
    }

    #[test]
    fn currency_to_base_conversion() {
        use super::*;
        let mut conversion: HashMap<Currency, CurrencyFactors> = HashMap::new();
        let inr_conversion_rates =
            CurrencyFactors::new(Decimal::new(823173, 4), Decimal::new(1214, 5));
        let usd_conversion_rates = CurrencyFactors::new(Decimal::new(1, 0), Decimal::new(1, 0));
        let convert_from = Currency::INR;
        let convert_to = Currency::USD;
        let amount = 2000;
        let base_currency = Currency::USD;
        conversion.insert(convert_from, inr_conversion_rates);
        conversion.insert(convert_to, usd_conversion_rates);
        let sample_rate = ExchangeRates::new(base_currency, conversion);
        let res =
            convert(&sample_rate, convert_from, convert_to, amount).expect("converted_currency");
        println!("The conversion from {amount} {convert_from} to {convert_to} is {res:?}");
    }

    #[test]
    fn base_to_currency_conversion() {
        use super::*;
        let mut conversion: HashMap<Currency, CurrencyFactors> = HashMap::new();
        let inr_conversion_rates =
            CurrencyFactors::new(Decimal::new(823173, 4), Decimal::new(1214, 5));
        let usd_conversion_rates = CurrencyFactors::new(Decimal::new(1, 0), Decimal::new(1, 0));
        let convert_from = Currency::USD;
        let convert_to = Currency::INR;
        let amount = 2000;
        let base_currency = Currency::USD;
        conversion.insert(convert_from, usd_conversion_rates);
        conversion.insert(convert_to, inr_conversion_rates);
        let sample_rate = ExchangeRates::new(base_currency, conversion);
        let res =
            convert(&sample_rate, convert_from, convert_to, amount).expect("converted_currency");
        println!("The conversion from {amount} {convert_from} to {convert_to} is {res:?}");
    }
}


pub struct Billing {
    date_of_birth: Option<Secret<String>>,
    email: Option<Email>,
    first_name: Option<Secret<String>>,
    last_name: Option<Secret<String>>,
    phone_number: Option<Secret<String>>,
    address: Option<AddressAirwallex>,
}


pub struct WellsfargoPaymentsIncrementalAuthorizationResponse {
    status: WellsfargoIncrementalAuthorizationStatus,
    error_information: Option<WellsfargoErrorInformation>,
}


pub struct StripePaymentListConstraints {
    pub customer: Option<id_type::CustomerId>,
    pub starting_after: Option<id_type::PaymentId>,
    pub ending_before: Option<id_type::PaymentId>,
    #[serde(default = "default_limit")]
    pub limit: u32,
    pub created: Option<i64>,
    #[serde(rename = "created[lt]")]
    pub created_lt: Option<i64>,
    #[serde(rename = "created[gt]")]
    pub created_gt: Option<i64>,
    #[serde(rename = "created[lte]")]
    pub created_lte: Option<i64>,
    #[serde(rename = "created[gte]")]
    pub created_gte: Option<i64>,
}


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

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

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

impl ConnectorSpecifications for for Payload


/// Panics if `json_payload` array does not contain one `GetPaymentMetricRequest` element.
    pub async fn get_profile_payment_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetPaymentMetricRequest; 1]>,
    ) -> impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetPaymentMetricRequest");
        let flow = AnalyticsFlow::GetPaymentMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let profile_id = auth
                    .profile_id
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?;
                let auth: AuthInfo = AuthInfo::ProfileLevel {
                    org_id: org_id.clone(),
                    merchant_id: merchant_id.clone(),
                    profile_ids: vec![profile_id.clone()],
                };

                let validator_response = request_validator(
                    AnalyticsRequest {
                        payment_attempt: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub fn get_inner_value(self) -> Vec<Operation> {
        self.0
    }


impl api::PaymentToken for Worldline {}

impl api::PaymentToken for for Worldline


pub async fn contract_based_routing_update_configs(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<routing_types::DynamicRoutingUpdateConfigQuery>,
    json_payload: web::Json<routing_types::ContractBasedRoutingConfig>,
) -> impl Responder {
    let flow = Flow::UpdateDynamicRoutingConfigs;
    let routing_payload_wrapper = routing_types::ContractBasedRoutingPayloadWrapper {
        updated_config: json_payload.into_inner(),
        algorithm_id: path.algorithm_id.clone(),
        profile_id: path.profile_id.clone(),
    };
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        routing_payload_wrapper.clone(),
        |state,
         auth: auth::AuthenticationData,
         wrapper: routing_types::ContractBasedRoutingPayloadWrapper,
         _| async {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            Box::pin(routing::contract_based_routing_update_configs(
                state,
                wrapper.updated_config,
                merchant_context,
                wrapper.algorithm_id,
                wrapper.profile_id,
            ))
            .await
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuthProfileFromRoute {
                profile_id: routing_payload_wrapper.profile_id,
                required_permission: Permission::ProfileRoutingWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub fn new() -> &'static Self {
        &Self {
            amount_converter: &StringMajorUnitForConnector,
        }
    }


pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<PaymentIntent> {
        generics::generic_insert(conn, self).await
    }


pub fn check_tenant(
    token_tenant_id: Option<id_type::TenantId>,
    header_tenant_id: &id_type::TenantId,
) -> RouterResult<()> {
    if let Some(tenant_id) = token_tenant_id {
        if tenant_id != *header_tenant_id {
            return Err(ApiErrorResponse::InvalidJwtToken).attach_printable(format!(
                "Token tenant ID: '{}' does not match Header tenant ID: '{}'",
                tenant_id.get_string_repr().to_owned(),
                header_tenant_id.get_string_repr().to_owned()
            ));
        }
    }
    Ok(())
}