File: crates/analytics/src/disputes/metrics/total_dispute_lost_amount.rs

use std::collections::HashSet;

use api_models::analytics::{
    disputes::{DisputeDimensions, DisputeFilters, DisputeMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::DisputeMetricRow;
use crate::{
    enums::AuthInfo,
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};
#[derive(Default)]
pub(super) struct TotalDisputeLostAmount {}

#[async_trait::async_trait]
impl<T> super::DisputeMetric<T> for TotalDisputeLostAmount
where
    T: AnalyticsDataSource + super::DisputeMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[DisputeDimensions],
        auth: &AuthInfo,
        filters: &DisputeFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(DisputeMetricsBucketIdentifier, DisputeMetricRow)>>
    where
        T: AnalyticsDataSource + super::DisputeMetricAnalytics,
    {
        let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::Dispute);

        for dim in dimensions.iter() {
            query_builder.add_select_column(dim).switch()?;
        }

        query_builder
            .add_select_column(Aggregate::Sum {
                field: "dispute_amount",
                alias: Some("total"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

        filters.set_filter_clause(&mut query_builder).switch()?;

        auth.set_filter_clause(&mut query_builder).switch()?;

        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;

        for dim in dimensions.iter() {
            query_builder.add_group_by_clause(dim).switch()?;
        }

        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .switch()?;
        }

        query_builder
            .add_filter_clause("dispute_status", "dispute_lost")
            .switch()?;

        query_builder
            .execute_query::<DisputeMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    DisputeMetricsBucketIdentifier::new(
                        i.dispute_stage.as_ref().map(|i| i.0),
                        i.connector.clone(),
                        i.currency.as_ref().map(|i| i.0),
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<HashSet<_>, crate::query::PostProcessingError>>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


pub async fn schedule_refund_execution(
    state: &SessionState,
    refund: diesel_refund::Refund,
    refund_type: api_models::refunds::RefundType,
    merchant_context: &domain::MerchantContext,
    payment_attempt: &storage::PaymentAttempt,
    payment_intent: &storage::PaymentIntent,
    merchant_connector_details: Option<common_types::domain::MerchantConnectorAuthDetails>,
) -> errors::RouterResult<diesel_refund::Refund> {
    let db = &*state.store;
    let runner = storage::ProcessTrackerRunner::RefundWorkflowRouter;
    let task = "EXECUTE_REFUND";
    let task_id = format!("{runner}_{task}_{}", refund.id.get_string_repr());

    let refund_process = db
        .find_process_by_id(&task_id)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to find the process id")?;

    let result = match refund.refund_status {
        enums::RefundStatus::Pending | enums::RefundStatus::ManualReview => {
            match (refund.sent_to_gateway, refund_process) {
                (false, None) => {
                    // Execute the refund task based on refund_type
                    match refund_type {
                        api_models::refunds::RefundType::Scheduled => {
                            add_refund_execute_task(db, &refund, runner)
                                .await
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable_lazy(|| format!("Failed while pushing refund execute task to scheduler, refund_id: {}", refund.id.get_string_repr()))?;

                            Ok(refund)
                        }
                        api_models::refunds::RefundType::Instant => {
                            let update_refund =
                                if state.conf.merchant_id_auth.merchant_id_auth_enabled {
                                    let merchant_connector_details =
                                        match merchant_connector_details {
                                            Some(details) => details,
                                            None => {
                                                return Err(report!(
                                            errors::ApiErrorResponse::MissingRequiredField {
                                                field_name: "merchant_connector_details"
                                            }
                                        ));
                                            }
                                        };
                                    Box::pin(internal_trigger_refund_to_gateway(
                                        state,
                                        &refund,
                                        merchant_context,
                                        payment_attempt,
                                        payment_intent,
                                        merchant_connector_details,
                                    ))
                                    .await
                                } else {
                                    Box::pin(trigger_refund_to_gateway(
                                        state,
                                        &refund,
                                        merchant_context,
                                        payment_attempt,
                                        payment_intent,
                                    ))
                                    .await
                                };

                            match update_refund {
                                Ok(updated_refund_data) => {
                                    add_refund_sync_task(db, &updated_refund_data, runner)
                                        .await
                                        .change_context(errors::ApiErrorResponse::InternalServerError)
                                        .attach_printable_lazy(|| format!(
                                            "Failed while pushing refund sync task in scheduler: refund_id: {}",
                                            refund.id.get_string_repr()
                                        ))?;
                                    Ok(updated_refund_data)
                                }
                                Err(err) => Err(err),
                            }
                        }
                    }
                }
                _ => {
                    // Sync the refund for status check
                    //[#300]: return refund status response
                    match refund_type {
                        api_models::refunds::RefundType::Scheduled => {
                            add_refund_sync_task(db, &refund, runner)
                                .await
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable_lazy(|| format!("Failed while pushing refund sync task in scheduler: refund_id: {}", refund.id.get_string_repr()))?;
                            Ok(refund)
                        }
                        api_models::refunds::RefundType::Instant => {
                            // [#255]: This is not possible in schedule_refund_execution as it will always be scheduled
                            // sync_refund_with_gateway(data, &refund).await
                            Ok(refund)
                        }
                    }
                }
            }
        }
        //  [#255]: This is not allowed to be otherwise or all
        _ => Ok(refund),
    }?;
    Ok(result)
}


pub struct ProdIntent {
    pub legal_business_name: Option<String>,
    pub business_label: Option<String>,
    pub business_location: Option<CountryAlpha2>,
    pub display_name: Option<String>,
    pub poc_email: Option<Secret<String>>,
    pub business_type: Option<String>,
    pub business_identifier: Option<String>,
    pub business_website: Option<String>,
    pub poc_name: Option<Secret<String>>,
    pub poc_contact: Option<Secret<String>>,
    pub comments: Option<String>,
    pub is_completed: bool,
    #[serde(default)]
    pub product_type: MerchantProductType,
    pub business_country_name: Option<String>,
}


pub struct ProfileUpdate {
    /// The name of profile
    #[schema(max_length = 64)]
    pub profile_name: Option<String>,

    /// The URL to redirect after the completion of the operation
    #[schema(value_type = Option<String>, max_length = 255, example = "https://www.example.com/success")]
    pub return_url: Option<common_utils::types::Url>,

    /// A boolean value to indicate if payment response hash needs to be enabled
    #[schema(default = true, example = true)]
    pub enable_payment_response_hash: Option<bool>,

    /// Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.
    pub payment_response_hash_key: Option<String>,

    /// A boolean value to indicate if redirect to merchant with http post needs to be enabled
    #[schema(default = false, example = true)]
    pub redirect_to_merchant_with_http_post: Option<bool>,

    /// Webhook related details
    pub webhook_details: Option<WebhookDetails>,

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

    /// Will be used to determine the time till which your payment will be active once the payment session starts
    #[schema(value_type = Option<u32>, example = 900)]
    pub order_fulfillment_time: Option<OrderFulfillmentTime>,

    /// Whether the order fulfillment time is calculated from the origin or the time of creating the payment, or confirming the payment
    #[schema(value_type = Option<OrderFulfillmentTimeOrigin>, example = "create")]
    pub order_fulfillment_time_origin: Option<api_enums::OrderFulfillmentTimeOrigin>,

    /// Verified Apple Pay domains for a particular profile
    pub applepay_verified_domains: Option<Vec<String>>,

    /// Client Secret Default expiry for all payments created under this profile
    #[schema(example = 900)]
    pub session_expiry: Option<u32>,

    /// Default Payment Link config for all payment links created under this profile
    pub payment_link_config: Option<BusinessPaymentLinkConfig>,

    /// External 3DS authentication details
    pub authentication_connector_details: Option<AuthenticationConnectorDetails>,

    /// Merchant's config to support extended card info feature
    pub extended_card_info_config: Option<ExtendedCardInfoConfig>,

    // Whether to use the billing details passed when creating the intent as payment method billing
    pub use_billing_as_payment_method_billing: Option<bool>,

    /// A boolean value to indicate if customer shipping details needs to be collected from wallet
    /// connector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)
    #[schema(default = false, example = false)]
    pub collect_shipping_details_from_wallet_connector_if_required: Option<bool>,

    /// A boolean value to indicate if customer billing details needs to be collected from wallet
    /// connector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)
    #[schema(default = false, example = false)]
    pub collect_billing_details_from_wallet_connector_if_required: Option<bool>,

    /// A boolean value to indicate if customer shipping details needs to be collected from wallet
    /// connector irrespective of connector required fields (Eg. Apple pay, Google pay etc)
    #[schema(default = false, example = false)]
    pub always_collect_shipping_details_from_wallet_connector: Option<bool>,

    /// A boolean value to indicate if customer billing details needs to be collected from wallet
    /// connector irrespective of connector required fields (Eg. Apple pay, Google pay etc)
    #[schema(default = false, example = false)]
    pub always_collect_billing_details_from_wallet_connector: Option<bool>,

    /// Indicates if the MIT (merchant initiated transaction) payments can be made connector
    /// agnostic, i.e., MITs may be processed through different connector than CIT (customer
    /// initiated transaction) based on the routing rules.
    /// If set to `false`, MIT will go through the same connector as the CIT.
    pub is_connector_agnostic_mit_enabled: Option<bool>,

    /// Default payout link config
    #[schema(value_type = Option<BusinessPayoutLinkConfig>)]
    pub payout_link_config: Option<BusinessPayoutLinkConfig>,

    /// These key-value pairs are sent as additional custom headers in the outgoing webhook request. It is recommended not to use more than four key-value pairs.
    #[schema(value_type = Option<Object>, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)]
    pub outgoing_webhook_custom_http_headers: Option<HashMap<String, String>>,

    /// Merchant Connector id to be stored for tax_calculator connector
    #[schema(value_type = Option<String>)]
    pub tax_connector_id: Option<id_type::MerchantConnectorAccountId>,

    /// Indicates if tax_calculator connector is enabled or not.
    /// If set to `true` tax_connector_id will be checked.
    pub is_tax_connector_enabled: Option<bool>,

    /// Indicates if network tokenization is enabled or not.
    pub is_network_tokenization_enabled: Option<bool>,

    /// Indicates if click to pay is enabled or not.
    #[schema(default = false, example = false)]
    pub is_click_to_pay_enabled: Option<bool>,

    /// Product authentication ids
    #[schema(value_type = Option<Object>, example = r#"{ "click_to_pay": "mca_ushduqwhdohwd", "netcetera": "mca_kwqhudqwd" }"#)]
    pub authentication_product_ids:
        Option<common_types::payments::AuthenticationConnectorAccountMap>,

    /// Card Testing Guard Configs
    pub card_testing_guard_config: Option<CardTestingGuardConfig>,

    ///Indicates if clear pan retries is enabled or not.
    pub is_clear_pan_retries_enabled: Option<bool>,

    /// Indicates if debit routing is enabled or not
    #[schema(value_type = Option<bool>)]
    pub is_debit_routing_enabled: Option<bool>,

    //Merchant country for the profile
    #[schema(value_type = Option<CountryAlpha2>, example = "US")]
    pub merchant_business_country: Option<api_enums::CountryAlpha2>,

    /// Indicates if external vault is enabled or not.
    pub is_external_vault_enabled: Option<bool>,

    /// External Vault Connector Details
    pub external_vault_connector_details: Option<ExternalVaultConnectorDetails>,

    /// Four-digit code assigned based on business type to determine processing fees and risk level
    #[schema(value_type = Option<MerchantCategoryCode>, example = "5411")]
    pub merchant_category_code: Option<api_enums::MerchantCategoryCode>,

    /// Merchant country code.
    /// This is a 3-digit ISO 3166-1 numeric country code that represents the country in which the merchant is registered or operates.
    /// Merchants typically receive this value based on their business registration information or during onboarding via payment processors or acquiring banks.
    /// It is used in payment processing, fraud detection, and regulatory compliance to determine regional rules and routing behavior.
    #[schema(value_type = Option<MerchantCountryCode>, example = "840")]
    pub merchant_country_code: Option<common_types::payments::MerchantCountryCode>,

    /// Indicates the state of revenue recovery algorithm type
    #[schema(value_type = Option<RevenueRecoveryAlgorithmType>, example = "cascading")]
    pub revenue_recovery_retry_algorithm_type:
        Option<common_enums::enums::RevenueRecoveryAlgorithmType>,

    /// Enable split payments, i.e., split the amount between multiple payment methods
    #[schema(value_type = Option<SplitTxnsEnabled>, default = "skip")]
    pub split_txns_enabled: Option<common_enums::SplitTxnsEnabled>,
}


pub fn make_dsl_input_for_payouts(
    payout_data: &payouts::PayoutData,
) -> RoutingResult<dsl_inputs::BackendInput> {
    let mandate = dsl_inputs::MandateData {
        mandate_acceptance_type: None,
        mandate_type: None,
        payment_type: None,
    };
    let metadata = payout_data
        .payouts
        .metadata
        .clone()
        .map(|val| val.parse_value("routing_parameters"))
        .transpose()
        .change_context(errors::RoutingError::MetadataParsingError)
        .attach_printable("Unable to parse routing_parameters from metadata of payouts")
        .unwrap_or(None);
    let payment = dsl_inputs::PaymentInput {
        amount: payout_data.payouts.amount,
        card_bin: None,
        currency: payout_data.payouts.destination_currency,
        authentication_type: None,
        capture_method: None,
        business_country: payout_data
            .payout_attempt
            .business_country
            .map(api_enums::Country::from_alpha2),
        billing_country: payout_data
            .billing_address
            .as_ref()
            .and_then(|ba| ba.address.as_ref())
            .and_then(|addr| addr.country)
            .map(api_enums::Country::from_alpha2),
        business_label: payout_data.payout_attempt.business_label.clone(),
        setup_future_usage: None,
    };
    let payment_method = dsl_inputs::PaymentMethodInput {
        payment_method: payout_data
            .payouts
            .payout_type
            .map(api_enums::PaymentMethod::foreign_from),
        payment_method_type: payout_data
            .payout_method_data
            .as_ref()
            .map(api_enums::PaymentMethodType::foreign_from)
            .or_else(|| {
                payout_data.payment_method.as_ref().and_then(|pm| {
                    #[cfg(feature = "v1")]
                    {
                        pm.payment_method_type
                    }
                    #[cfg(feature = "v2")]
                    {
                        pm.payment_method_subtype
                    }
                })
            }),
        card_network: None,
    };
    Ok(dsl_inputs::BackendInput {
        mandate,
        metadata,
        payment,
        payment_method,
        acquirer_data: None,
        customer_device_data: None,
        issuer_data: None,
    })
}


impl api::Payment for HyperswitchVault {}

impl api::Payment for for HyperswitchVault


impl std::fmt::Display for Op<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Op::Insert => f.write_str("insert"),
            Op::Find => f.write_str("find"),
            Op::Update(p_key, _, updated_by) => {
                f.write_str(&format!("update_{p_key} for updated_by_{updated_by:?}"))
            }
        }
    }
}


impl IncomingWebhook for Payone {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, ConnectorError> {
        Err(report!(ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, ConnectorError> {
        Err(report!(ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, ConnectorError> {
        Err(report!(ConnectorError::WebhooksNotImplemented))
    }
}


impl api::PaymentSession for Shift4 {}


### Bug Fixes

- **users:** Fallback to fresh lineage context if cached role_id no longer matches ([#8593](https://github.com/juspay/hyperswitch/pull/8593)) ([`aaa4fca`](https://github.com/juspay/hyperswitch/commit/aaa4fca58d8eaab0eb0163aad226427eebf7c7cd))

### Refactors

- **dynamic_routing:** Make the dynamo configs optional ([#8589](https://github.com/juspay/hyperswitch/pull/8589)) ([`4048aa1`](https://github.com/juspay/hyperswitch/commit/4048aa16cadd98605b81aebc7543da50c36454b9))

### Miscellaneous Tasks

- **stripe:** Eps bank_name should be mandatory ([#8577](https://github.com/juspay/hyperswitch/pull/8577)) ([`fffaa53`](https://github.com/juspay/hyperswitch/commit/fffaa53a5fe4859bea607f2eb6bf866b4a20b949))

**Full Changelog:** [`2025.07.14.1...2025.07.15.0`](https://github.com/juspay/hyperswitch/compare/2025.07.14.1...2025.07.15.0)

- - -

## 2025.07.14.1

### Features

- **connector:** [AUTHORIZEDOTNET] Populated merchant required fields in setupmandate flow ([#8618](https://github.com/juspay/hyperswitch/pull/8618)) ([`cb8dff0`](https://github.com/juspay/hyperswitch/commit/cb8dff008c71586713973d350e7e3789e82085ec))

**Full Changelog:** [`2025.07.14.0...2025.07.14.1`](https://github.com/juspay/hyperswitch/compare/2025.07.14.0...2025.07.14.1)

- - -

## 2025.07.14.0

### Features

- **connector:**
  - [AIRWALLEX] - Added Paypal, Trustly, Klarna , Atome, Blik Payment Methods ([#8475](https://github.com/juspay/hyperswitch/pull/8475)) ([`d5f5527`](https://github.com/juspay/hyperswitch/commit/d5f5527499c11ab427bd07c326cc294fc354f342))
  - [payload] add webhook support ([#8558](https://github.com/juspay/hyperswitch/pull/8558)) ([`2fe3132`](https://github.com/juspay/hyperswitch/commit/2fe3132da8eedea39018e5c6afec9ab4dd49ddd6))
- **core:** Hyperswitch <|> UCS integration v2 ([#8439](https://github.com/juspay/hyperswitch/pull/8439)) ([`ae9feca`](https://github.com/juspay/hyperswitch/commit/ae9feca82cdc29ff442276284f2aeb4ad01e94ba))
- **payments:** Propagate additional payment method data for apple pay during MIT ([#7170](https://github.com/juspay/hyperswitch/pull/7170)) ([`0f70fc5`](https://github.com/juspay/hyperswitch/commit/0f70fc512c90e4d5603af7b965c137465999b73c))

### Refactors

- **cypress:** Update skip logic and test flow for cypress incremental auth tests ([#8594](https://github.com/juspay/hyperswitch/pull/8594)) ([`3c49871`](https://github.com/juspay/hyperswitch/commit/3c498714e76b400657fc0fc25b7559ca3dca1908))

**Full Changelog:** [`2025.07.11.0...2025.07.14.0`](https://github.com/juspay/hyperswitch/compare/2025.07.11.0...2025.07.14.0)

- - -

## 2025.07.11.0

### Miscellaneous Tasks

- Address Rust 1.88.0 clippy lints ([#8607](https://github.com/juspay/hyperswitch/pull/8607)) ([`b1c9be1`](https://github.com/juspay/hyperswitch/commit/b1c9be1600dbd897c32adcc2d8b98e77ec67c516))

**Full Changelog:** [`2025.07.10.0...2025.07.11.0`](https://github.com/juspay/hyperswitch/compare/2025.07.10.0...2025.07.11.0)

- - -

## 2025.07.10.0

### Refactors

- **connector:** Move connector mappings and endpoints to dedicated modules ([#8562](https://github.com/juspay/hyperswitch/pull/8562)) ([`99885b6`](https://github.com/juspay/hyperswitch/commit/99885b699d7524e87697f78608e6f2b36c9d8a8e))

**Full Changelog:** [`2025.07.09.0...2025.07.10.0`](https://github.com/juspay/hyperswitch/compare/2025.07.09.0...2025.07.10.0)

- - -

## 2025.07.09.0

### Features

- **connector:**
  - [silverflow] template code ([#8553](https://github.com/juspay/hyperswitch/pull/8553)) ([`c322eb9`](https://github.com/juspay/hyperswitch/commit/c322eb9cabb2ca7a8ad0c3ed57192dde9bf7eaa4))
  - [AUTHIPAY] Integrate cards non 3ds payments ([#8266](https://github.com/juspay/hyperswitch/pull/8266)) ([`ef42ad4`](https://github.com/juspay/hyperswitch/commit/ef42ad43120d8699876990718235b518fa5011ac))

### Bug Fixes

- **payment_method:** Update entity id used for Vault to global customer id ([#8380](https://github.com/juspay/hyperswitch/pull/8380)) ([`cf96c82`](https://github.com/juspay/hyperswitch/commit/cf96c826cad08ea84148238b73bd9ca2de8c0518))

### Refactors

- **routing:** Add conditional check for invoking DE routing flows ([#8559](https://github.com/juspay/hyperswitch/pull/8559)) ([`7508c80`](https://github.com/juspay/hyperswitch/commit/7508c801580f8d83b203b34165dacb6a3814d941))

**Full Changelog:** [`2025.07.08.0...2025.07.09.0`](https://github.com/juspay/hyperswitch/compare/2025.07.08.0...2025.07.09.0)

- - -

## 2025.07.08.0

### Features

- **connector:** [Celero] add Connector Template Code ([#8489](https://github.com/juspay/hyperswitch/pull/8489)) ([`dfed2be`](https://github.com/juspay/hyperswitch/commit/dfed2be2900a53f62a4903376004a264f9da25a8))
- **connectors:** [worldpayvantiv] add connector mandate support ([#8546](https://github.com/juspay/hyperswitch/pull/8546)) ([`de92973`](https://github.com/juspay/hyperswitch/commit/de92973b01c764474c52185842677e2d32058e8d))
- **cypress:** [worldpayvantiv] add cypress test ([#8234](https://github.com/juspay/hyperswitch/pull/8234)) ([`102f278`](https://github.com/juspay/hyperswitch/commit/102f2785199a2f66d9cd3428678f54ba3f015ff7))
- **payment-methods:** Create payment_token in vault confirm / do payment-confirm with temp token from session ([#8525](https://github.com/juspay/hyperswitch/pull/8525)) ([`4aca455`](https://github.com/juspay/hyperswitch/commit/4aca45531b1616a2a2cc028878a96793148dff7f))

**Full Changelog:** [`2025.07.07.0...2025.07.08.0`](https://github.com/juspay/hyperswitch/compare/2025.07.07.0...2025.07.08.0)

- - -

## 2025.07.07.0

### Features

- **connector:** [payload] introduce no-3ds cards ([#8545](https://github.com/juspay/hyperswitch/pull/8545)) ([`baad3f6`](https://github.com/juspay/hyperswitch/commit/baad3f6a37c1c0d948c415f281b6552c5b51e376))
- **core:** Hyperswitch <|> UCS Integration ([#8280](https://github.com/juspay/hyperswitch/pull/8280)) ([`f6574b7`](https://github.com/juspay/hyperswitch/commit/f6574b78288074d1806e5ea78117f89e3e19ae9f))
- **debit_routing:** Add `debit_routing_savings` in analytics payment attempt ([#8519](https://github.com/juspay/hyperswitch/pull/8519)) ([`fc3c64f`](https://github.com/juspay/hyperswitch/commit/fc3c64fad37a4434f103fb0bcdf1eafe67441b56))

### Bug Fixes

- **payout:** Use get_string_repr for formatting payout IDs ([#8547](https://github.com/juspay/hyperswitch/pull/8547)) ([`eb94cfe`](https://github.com/juspay/hyperswitch/commit/eb94cfe7e558348e8da3aeaaa01224a1ea2dbbe2))

### Refactors

- **connector:** [Worldpayvantiv] refactor void flow and handle transaction status ([#8540](https://github.com/juspay/hyperswitch/pull/8540)) ([`41291e5`](https://github.com/juspay/hyperswitch/commit/41291e5cde18c08b4586f3d37a9a858bdcda58b0))
- Extract connector auth and metadata validation into separate module ([#8501](https://github.com/juspay/hyperswitch/pull/8501)) ([`26ae469`](https://github.com/juspay/hyperswitch/commit/26ae469fafcec5c70ca2db36e438170ba8b8aa01))

**Full Changelog:** [`2025.07.04.0...2025.07.07.0`](https://github.com/juspay/hyperswitch/compare/2025.07.04.0...2025.07.07.0)

- - -

## 2025.07.04.0

### Features

- **connector:**
  - [shift4] Boleto, Trustly, Alipay, Wechatpay PMs added ([#8476](https://github.com/juspay/hyperswitch/pull/8476)) ([`ac3b2d4`](https://github.com/juspay/hyperswitch/commit/ac3b2d4055cee0ae71a12fcb9119f4ba288417d4))
  - [payload] template code ([#8526](https://github.com/juspay/hyperswitch/pull/8526)) ([`7f5ec74`](https://github.com/juspay/hyperswitch/commit/7f5ec7439b3469df7d77b8151ae3f4218847641a))
  - [AUTHORIZEDOTNET] Add AVS checks ([#8511](https://github.com/juspay/hyperswitch/pull/8511)) ([`417039d`](https://github.com/juspay/hyperswitch/commit/417039d175c429c687f70cbb18625ef0c98c19ed))
  - [shift4] Blik, Klarna, Bitpay PMs added ([#8478](https://github.com/juspay/hyperswitch/pull/8478)) ([`37a95e3`](https://github.com/juspay/hyperswitch/commit/37a95e3733676b24d6fa6924dcd84736eeca9bb4))
  - [Redsys] Use merchant payment_id for ds_merchant_order with length check ([#8485](https://github.com/juspay/hyperswitch/pull/8485)) ([`6678ee3`](https://github.com/juspay/hyperswitch/commit/6678ee351748e5629a22e84677d58297cbac62a5))
- **core:** Populate connector raw response and connector_response_reference_id for razorpay ([#8499](https://github.com/juspay/hyperswitch/pull/8499)) ([`2253d98`](https://github.com/juspay/hyperswitch/commit/2253d981c89899e6e838f2072f46388eaa0bd8a0))

### Bug Fixes

- **connector:** [CYBERSOURCE] Passing pares_status for MasterCard & Visa and transaction_type field ([#8518](https://github.com/juspay/hyperswitch/pull/8518)) ([`721f780`](https://github.com/juspay/hyperswitch/commit/721f780c6baf5da5589d2a3242488b0d4383a1d8))
- **core:** Update error for routing instead of Internal Server Error ([#8512](https://github.com/juspay/hyperswitch/pull/8512)) ([`e4b1d45`](https://github.com/juspay/hyperswitch/commit/e4b1d45be76fb6183474177a1eaee26340b2e7d8))
- Update routing_approach for session_token flow ([#8490](https://github.com/juspay/hyperswitch/pull/8490)) ([`c275e13`](https://github.com/juspay/hyperswitch/commit/c275e13caf22362173e77d2260b440159abcabb3))

**Full Changelog:** [`2025.07.03.0...2025.07.04.0`](https://github.com/juspay/hyperswitch/compare/2025.07.03.0...2025.07.04.0)

- - -

## 2025.07.03.0

### Features

- **connector:**
  - `multisafepay` added payment methods | TRUSTLY | WeChatpay | Alipay ([#8465](https://github.com/juspay/hyperswitch/pull/8465)) ([`01bd831`](https://github.com/juspay/hyperswitch/commit/01bd831665829bd318da5afe7410893ed5afc51e))
  - [CHECKBOOK] Add Template Code ([#8494](https://github.com/juspay/hyperswitch/pull/8494)) ([`95077c6`](https://github.com/juspay/hyperswitch/commit/95077c64e27d6df35c15465f844c6ad7e1945571))
- **masking:** Implement `prost::Message` trait for `Secret` and `StrongSecret` types ([#8458](https://github.com/juspay/hyperswitch/pull/8458)) ([`ad29631`](https://github.com/juspay/hyperswitch/commit/ad29631c537c92fb11f1827fb3f6737cdc741f0f))

### Miscellaneous Tasks

- Address Rust 1.88.0 clippy lints ([#8498](https://github.com/juspay/hyperswitch/pull/8498)) ([`20b52f1`](https://github.com/juspay/hyperswitch/commit/20b52f11c3c010113a37a0b893c39ab0e8b5bfd1))

**Full Changelog:** [`2025.07.02.0...2025.07.03.0`](https://github.com/juspay/hyperswitch/compare/2025.07.02.0...2025.07.03.0)

- - -

## 2025.07.02.0

### Features

- **connector:**
  - Implement capture and webhook flow, fix some issues in ACI ([#8349](https://github.com/juspay/hyperswitch/pull/8349)) ([`1ae3024`](https://github.com/juspay/hyperswitch/commit/1ae30247ca1fe94bf72752906462521b58513ca7))
  - [ADYENPLATFORM] add card payouts ([#8504](https://github.com/juspay/hyperswitch/pull/8504)) ([`0c64915`](https://github.com/juspay/hyperswitch/commit/0c649158a8ee491e2ff6ff37e922266d5b64b22d))
  - [DWOLLA] - Add template code ([#8496](https://github.com/juspay/hyperswitch/pull/8496)) ([`ad52251`](https://github.com/juspay/hyperswitch/commit/ad522513b9c7cd978f79dc1f00e25f9dcabb58dc))
  - [SANTANDER] Added Authorize, PSync, Void, Refund & RSync Flows for Pix QR Code Bank Transfer ([#8463](https://github.com/juspay/hyperswitch/pull/8463)) ([`28d6357`](https://github.com/juspay/hyperswitch/commit/28d63575e63d87b1cba7c6215faee803f2fff7d8))
- **connectors:** [Worldpayvantiv] add NTI flow and refactor sync flows ([#8495](https://github.com/juspay/hyperswitch/pull/8495)) ([`f8dc3ec`](https://github.com/juspay/hyperswitch/commit/f8dc3ecfe6a9cb1441228dce474b92c4a6fe2985))
- **data-migration:** Add connector customer and mandate details support for multiple profiles ([#8473](https://github.com/juspay/hyperswitch/pull/8473)) ([`ce2b90b`](https://github.com/juspay/hyperswitch/commit/ce2b90b3d37d9214ef4ad46d5748aa0c864d508d))
- **dummy_connector:** Allow a dummy connector to succeed a failed card ([#8469](https://github.com/juspay/hyperswitch/pull/8469)) ([`69ab255`](https://github.com/juspay/hyperswitch/commit/69ab255394fc537dbc40e01c72f27f3c8dbfb5f1))
- **payouts:** Add domain type for PayoutId ([#8395](https://github.com/juspay/hyperswitch/pull/8395)) ([`a6e3d2c`](https://github.com/juspay/hyperswitch/commit/a6e3d2c71eed53ab400628a482cc61aae3720340))

### Refactors

- **authentication:** Flattened paymentData in authentication trait functions ([#8365](https://github.com/juspay/hyperswitch/pull/8365)) ([`18a779f`](https://github.com/juspay/hyperswitch/commit/18a779f94d418587c10c8ac8766d22ff41d25f6a))
- **connector:** Update add connector script with new connector features ([#8213](https://github.com/juspay/hyperswitch/pull/8213)) ([`2ff93ff`](https://github.com/juspay/hyperswitch/commit/2ff93ff972453ce30ffeff667cb406d35286f60a))
- Exposed auth analytics at merchant,org and profile levels ([#8335](https://github.com/juspay/hyperswitch/pull/8335)) ([`e638f23`](https://github.com/juspay/hyperswitch/commit/e638f239d3f3a0e7a63a5e71a1df6f516ddf9c4a))

**Full Changelog:** [`2025.07.01.0...2025.07.02.0`](https://github.com/juspay/hyperswitch/compare/2025.07.01.0...2025.07.02.0)

- - -

## 2025.07.01.0

### Features

- **core:** Allow setting up status across payments, refunds and payouts for triggering webhooks in core resource flows ([#8433](https://github.com/juspay/hyperswitch/pull/8433)) ([`d305fad`](https://github.com/juspay/hyperswitch/commit/d305fad2e6c403fde11b9eea785fddefbf3477df))

**Full Changelog:** [`2025.06.30.0...2025.07.01.0`](https://github.com/juspay/hyperswitch/compare/2025.06.30.0...2025.07.01.0)

- - -

## 2025.06.30.0

### Features

- **openapi:** Add x-mcp extension to v1 spec ([#8443](https://github.com/juspay/hyperswitch/pull/8443)) ([`a685f6b`](https://github.com/juspay/hyperswitch/commit/a685f6b59cb95b4b8eda33e6951faa31091099a5))

### Bug Fixes

- **connector:** 2 digit state code for ach fixed ([#8466](https://github.com/juspay/hyperswitch/pull/8466)) ([`68db51a`](https://github.com/juspay/hyperswitch/commit/68db51a37a1bd4b4a69cb7d018217ae4fd8babac))
- **env:** Update env for network tokenization service ([#8472](https://github.com/juspay/hyperswitch/pull/8472)) ([`6c66c36`](https://github.com/juspay/hyperswitch/commit/6c66c36a66b6644c43761b7ac9c1326945a6a820))

**Full Changelog:** [`2025.06.27.0...2025.06.30.0`](https://github.com/juspay/hyperswitch/compare/2025.06.27.0...2025.06.30.0)

- - -

## 2025.06.27.0

### Features

- **core:** Accept merchant_connector_details in Refunds create and retrieve flow ([#8441](https://github.com/juspay/hyperswitch/pull/8441)) ([`b185d85`](https://github.com/juspay/hyperswitch/commit/b185d85f6b55a8a99d4942a7a0966f5f731a920c))
- Kv changes for V2 feature ([#8198](https://github.com/juspay/hyperswitch/pull/8198)) ([`d2740f0`](https://github.com/juspay/hyperswitch/commit/d2740f0322a0ec1327515c0159b64c930307f451))

### Bug Fixes

- **connector:** [TRUSTPAY] Consuming Amount in PSync Response ([#8455](https://github.com/juspay/hyperswitch/pull/8455)) ([`537d175`](https://github.com/juspay/hyperswitch/commit/537d1755329c372f318df24aeda984d22f0b7a19))

**Full Changelog:** [`2025.06.26.1...2025.06.27.0`](https://github.com/juspay/hyperswitch/compare/2025.06.26.1...2025.06.27.0)

- - -

## 2025.06.26.1

### Features

- **router:** Add webhooks for network tokenization ([#6695](https://github.com/juspay/hyperswitch/pull/6695)) ([`ec6d0e4`](https://github.com/juspay/hyperswitch/commit/ec6d0e4d62a530163ae1c806dcf40ccfd264b246))

### Bug Fixes

- **recovery:** Populate connector request reference id in revenue recovery record attempt flow. ([#8434](https://github.com/juspay/hyperswitch/pull/8434)) ([`f1c5336`](https://github.com/juspay/hyperswitch/commit/f1c53366b28a89c82db0416ffe3e56b8a704afdf))

### Build System / Dependencies

- **deps:** Bump tonic version from 0.12 to 0.13 ([#8461](https://github.com/juspay/hyperswitch/pull/8461)) ([`9e43592`](https://github.com/juspay/hyperswitch/commit/9e435929f0ef45a6324c8d260c74ae3268470e72))

**Full Changelog:** [`2025.06.26.0...2025.06.26.1`](https://github.com/juspay/hyperswitch/compare/2025.06.26.0...2025.06.26.1)

- - -

## 2025.06.26.0

### Features

- **connector:**
  - Add click to pay feature for trustpay ([#8304](https://github.com/juspay/hyperswitch/pull/8304)) ([`46709ae`](https://github.com/juspay/hyperswitch/commit/46709ae3695eb1a6601c356397545f86de9f0f64))
  - [jpmorgan] implement refund flow ([#8436](https://github.com/juspay/hyperswitch/pull/8436)) ([`cf92e1a`](https://github.com/juspay/hyperswitch/commit/cf92e1a5e10fe5e1a2f939038bf0a94bcaf01e92))

### Refactors

- **router:** Remove `refunds_v2` feature flag ([#8310](https://github.com/juspay/hyperswitch/pull/8310)) ([`c5c0e67`](https://github.com/juspay/hyperswitch/commit/c5c0e677f2a2d43170a66330c98e0ebc4d771717))
- Move CustomerAcceptance to common_types ([#8299](https://github.com/juspay/hyperswitch/pull/8299)) ([`44d93e5`](https://github.com/juspay/hyperswitch/commit/44d93e572f45c0ff46936c6f352c767f5cd7daf4))

### Miscellaneous Tasks

- **postman:** Update Postman collection files ([`6419ad8`](https://github.com/juspay/hyperswitch/commit/6419ad85fbc08246a207e2601a36e4661bf805ff))

**Full Changelog:** [`2025.06.25.0...2025.06.26.0`](https://github.com/juspay/hyperswitch/compare/2025.06.25.0...2025.06.26.0)

- - -

## 2025.06.25.0

### Features

- **authentication:** Initial commit to modular authentication create ([#8085](https://github.com/juspay/hyperswitch/pull/8085)) ([`2ea5d81`](https://github.com/juspay/hyperswitch/commit/2ea5d81d534840d8ff900b9d768c0c264c4ddf53))


pub struct ValidateResult {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: api::PaymentIdType,
    pub storage_scheme: enums::MerchantStorageScheme,
    pub requeue: bool,
}


impl std::fmt::Display for InvalidCrmConfig {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "crm: {}", self.0)
    }
}

impl std::fmt::Display for for InvalidCrmConfig


pub struct SdkEventMetricsAccumulator {
    pub payment_attempts: CountAccumulator,
    pub payment_methods_call_count: CountAccumulator,
    pub average_payment_time: CountAccumulator,
    pub load_time: CountAccumulator,
    pub sdk_initiated_count: CountAccumulator,
    pub sdk_rendered_count: CountAccumulator,
    pub payment_method_selected_count: CountAccumulator,
    pub payment_data_filled_count: CountAccumulator,
}


/// Derives the [`Serialize`][Serialize] implementation for error responses that are returned by
/// the API server.
///
/// This macro can be only used with enums. In addition to deriving [`Serialize`][Serialize], this
/// macro provides three methods: `error_type()`, `error_code()` and `error_message()`. Each enum
/// variant must have three required fields:
///
/// - `error_type`: This must be an enum variant which is returned by the `error_type()` method.
/// - `code`: A string error code, returned by the `error_code()` method.
/// - `message`: A string error message, returned by the `error_message()` method. The message
///   provided will directly be passed to `format!()`.
///
/// The return type of the `error_type()` method is provided by the `error_type_enum` field
/// annotated to the entire enum. Thus, all enum variants provided to the `error_type` field must
/// be variants of the enum provided to `error_type_enum` field. In addition, the enum passed to
/// the `error_type_enum` field must implement [`Serialize`][Serialize].
///
/// **NOTE:** This macro does not implement the [`Display`][Display] trait.
///
/// # Example
///
/// ```
/// use router_derive::ApiError;
///
/// #[derive(Clone, Debug, serde::Serialize)]
/// enum ErrorType {
///     StartupError,
///     InternalError,
///     SerdeError,
/// }
///
/// #[derive(Debug, ApiError)]
/// #[error(error_type_enum = ErrorType)]
/// enum MyError {
///     #[error(error_type = ErrorType::StartupError, code = "E001", message = "Failed to read configuration")]
///     ConfigurationError,
///     #[error(error_type = ErrorType::InternalError, code = "E002", message = "A database error occurred")]
///     DatabaseError,
///     #[error(error_type = ErrorType::SerdeError, code = "E003", message = "Failed to deserialize object")]
///     DeserializationError,
///     #[error(error_type = ErrorType::SerdeError, code = "E004", message = "Failed to serialize object")]
///     SerializationError,
/// }
///
/// impl ::std::fmt::Display for MyError {
///     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::result::Result<(), ::core::fmt::Error> {
///         f.write_str(&self.error_message())
///     }
/// }
/// ```
///
/// # The Generated `Serialize` Implementation
///
/// - For a simple enum variant with no fields, the generated [`Serialize`][Serialize]
///   implementation has only three fields, `type`, `code` and `message`:
///
/// ```
/// # use router_derive::ApiError;
/// # #[derive(Clone, Debug, serde::Serialize)]
/// # enum ErrorType {
/// #     StartupError,
/// # }
/// #[derive(Debug, ApiError)]
/// #[error(error_type_enum = ErrorType)]
/// enum MyError {
///     #[error(error_type = ErrorType::StartupError, code = "E001", message = "Failed to read configuration")]
///     ConfigurationError,
///     // ...
/// }
/// # impl ::std::fmt::Display for MyError {
/// #     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::result::Result<(), ::core::fmt::Error> {
/// #         f.write_str(&self.error_message())
/// #     }
/// # }
///
/// let json = serde_json::json!({
///     "type": "StartupError",
///     "code": "E001",
///     "message": "Failed to read configuration"
/// });
/// assert_eq!(serde_json::to_value(MyError::ConfigurationError).unwrap(), json);
/// ```
///
/// - For an enum variant with named fields, the generated [`Serialize`][Serialize] implementation
///   includes three mandatory fields, `type`, `code` and `message`, and any other fields not
///   included in the message:
///
/// ```
/// # use router_derive::ApiError;
/// # #[derive(Clone, Debug, serde::Serialize)]
/// # enum ErrorType {
/// #     StartupError,
/// # }
/// #[derive(Debug, ApiError)]
/// #[error(error_type_enum = ErrorType)]
/// enum MyError {
///     #[error(error_type = ErrorType::StartupError, code = "E001", message = "Failed to read configuration file: {file_path}")]
///     ConfigurationError { file_path: String, reason: String },
///     // ...
/// }
/// # impl ::std::fmt::Display for MyError {
/// #     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::result::Result<(), ::core::fmt::Error> {
/// #         f.write_str(&self.error_message())
/// #     }
/// # }
///
/// let json = serde_json::json!({
///     "type": "StartupError",
///     "code": "E001",
///     "message": "Failed to read configuration file: config.toml",
///     "reason": "File not found"
/// });
/// let error = MyError::ConfigurationError{
///     file_path: "config.toml".to_string(),
///     reason: "File not found".to_string(),
/// };
/// assert_eq!(serde_json::to_value(error).unwrap(), json);
/// ```
///
/// [Serialize]: https://docs.rs/serde/latest/serde/trait.Serialize.html
/// [Display]: ::core::fmt::Display
#[proc_macro_derive(ApiError, attributes(error))]
pub fn api_error_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);

    let tokens =
        macros::api_error_derive_inner(&ast).unwrap_or_else(|error| error.to_compile_error());
    tokens.into()
}


OpenAPI Block Path: components.schemas.CtpServiceDetails
{
  "type": "object",
  "properties": {
    "merchant_transaction_id": {
      "type": "string",
      "description": "merchant transaction id",
      "nullable": true
    },
    "correlation_id": {
      "type": "string",
      "description": "network transaction correlation id",
      "nullable": true
    },
    "x_src_flow_id": {
      "type": "string",
      "description": "session transaction flow id",
      "nullable": true
    },
    "provider": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CtpServiceProvider"
        }
      ],
      "nullable": true
    },
    "encrypted_payload": {
      "type": "string",
      "description": "Encrypted payload",
      "nullable": true
    }
  }
}


impl api::PaymentAuthorize for Trustpay {}

impl api::PaymentAuthorize for for Trustpay


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

pub use api_models::api_keys::{
    ApiKeyExpiration, CreateApiKeyRequest, CreateApiKeyResponse, ListApiKeyConstraints,
    RetrieveApiKeyResponse, RevokeApiKeyResponse, UpdateApiKeyRequest,
};


impl ConnectorValidation for Zen {
    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        // since we can make psync call with our reference_id, having connector_transaction_id is not an mandatory criteria
        Ok(())
    }
}


pub struct GlobalpayWebhookObjectEventType {
    pub status: GlobalpayWebhookStatus,
}


pub struct ItaubankTokenErrorResponse {
    pub status: i64,
    pub title: Option<String>,
    pub detail: Option<String>,
    pub user_message: Option<String>,
}


OpenAPI Block Path: components.schemas.PaymentsUpdateRequest
{
  "type": "object",
  "properties": {
    "amount": {
      "type": "integer",
      "format": "int64",
      "description": "The primary amount for the payment, provided in the lowest denomination of the specified currency (e.g., 6540 for $65.40 USD). This field is mandatory for creating a payment.",
      "example": 6540,
      "nullable": true,
      "minimum": 0
    },
    "order_tax_amount": {
      "type": "integer",
      "format": "int64",
      "description": "Total tax amount applicable to the order, in the lowest denomination of the currency.",
      "example": 6540,
      "nullable": true
    },
    "currency": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Currency"
        }
      ],
      "nullable": true
    },
    "amount_to_capture": {
      "type": "integer",
      "format": "int64",
      "description": "The amount to be captured from the user's payment method, in the lowest denomination. If not provided, and `capture_method` is `automatic`, the full payment `amount` will be captured. If `capture_method` is `manual`, this can be specified in the `/capture` call. Must be less than or equal to the authorized amount.",
      "example": 6540,
      "nullable": true
    },
    "shipping_cost": {
      "type": "integer",
      "format": "int64",
      "description": "The shipping cost for the payment. This is required for tax calculation in some regions.",
      "example": 6540,
      "nullable": true
    },
    "payment_id": {
      "type": "string",
      "description": "Optional. A merchant-provided unique identifier for the payment, contains 30 characters long (e.g., \"pay_mbabizu24mvu3mela5njyhpit4\"). If provided, it ensures idempotency for the payment creation request. If omitted, Hyperswitch generates a unique ID for the payment.",
      "example": "pay_mbabizu24mvu3mela5njyhpit4",
      "nullable": true,
      "maxLength": 30,
      "minLength": 30
    },
    "routing": {
      "allOf": [
        {
          "$ref": "#/components/schemas/StraightThroughAlgorithm"
        }
      ],
      "nullable": true
    },
    "connector": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Connector"
      },
      "description": "This allows to manually select a connector with which the payment can go through.",
      "example": [
        "stripe",
        "adyen"
      ],
      "nullable": true
    },
    "capture_method": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CaptureMethod"
        }
      ],
      "nullable": true
    },
    "authentication_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/AuthenticationType"
        }
      ],
      "default": "three_ds",
      "nullable": true
    },
    "billing": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Address"
        }
      ],
      "nullable": true
    },
    "confirm": {
      "type": "boolean",
      "description": "If set to `true`, Hyperswitch attempts to confirm and authorize the payment immediately after creation, provided sufficient payment method details are included. If `false` or omitted (default is `false`), the payment is created with a status such as `requires_payment_method` or `requires_confirmation`, and a separate `POST /payments/{payment_id}/confirm` call is necessary to proceed with authorization.",
      "default": false,
      "example": true,
      "nullable": true
    },
    "customer": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CustomerDetails"
        }
      ],
      "nullable": true
    },
    "customer_id": {
      "type": "string",
      "description": "The identifier for the customer",
      "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44",
      "nullable": true,
      "maxLength": 64,
      "minLength": 1
    },
    "off_session": {
      "type": "boolean",
      "description": "Set to true to indicate that the customer is not in your checkout flow during this payment, and therefore is unable to authenticate. This parameter is intended for scenarios where you collect card details and charge them later. When making a recurring payment by passing a mandate_id, this parameter is mandatory",
      "example": true,
      "nullable": true
    },
    "description": {
      "type": "string",
      "description": "An arbitrary string attached to the payment. Often useful for displaying to users or for your own internal record-keeping.",
      "example": "It's my first payment request",
      "nullable": true
    },
    "return_url": {
      "type": "string",
      "description": "The URL to redirect the customer to after they complete the payment process or authentication. This is crucial for flows that involve off-site redirection (e.g., 3DS, some bank redirects, wallet payments).",
      "example": "https://hyperswitch.io",
      "nullable": true,
      "maxLength": 2048
    },
    "setup_future_usage": {
      "allOf": [
        {
          "$ref": "#/components/schemas/FutureUsage"
        }
      ],
      "nullable": true
    },
    "payment_method_data": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentMethodDataRequest"
        }
      ],
      "nullable": true
    },
    "payment_method": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentMethod"
        }
      ],
      "nullable": true
    },
    "payment_token": {
      "type": "string",
      "description": "As Hyperswitch tokenises the sensitive details about the payments method, it provides the payment_token as a reference to a stored payment method, ensuring that the sensitive details are not exposed in any manner.",
      "example": "187282ab-40ef-47a9-9206-5099ba31e432",
      "nullable": true
    },
    "shipping": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Address"
        }
      ],
      "nullable": true
    },
    "statement_descriptor_name": {
      "type": "string",
      "description": "For non-card charges, you can use this value as the complete description that appears on your customers’ statements. Must contain at least one letter, maximum 22 characters.",
      "example": "Hyperswitch Router",
      "nullable": true,
      "maxLength": 255
    },
    "statement_descriptor_suffix": {
      "type": "string",
      "description": "Provides information about a card payment that customers see on their statements. Concatenated with the prefix (shortened descriptor) or statement descriptor that’s set on the account to form the complete statement descriptor. Maximum 22 characters for the concatenated descriptor.",
      "example": "Payment for shoes purchase",
      "nullable": true,
      "maxLength": 255
    },
    "order_details": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/OrderDetailsWithAmount"
      },
      "description": "Use this object to capture the details about the different products for which the payment is being made. The sum of amount across different products here should be equal to the overall payment amount",
      "example": "[{\n        \"product_name\": \"Apple iPhone 16\",\n        \"quantity\": 1,\n        \"amount\" : 69000\n        \"product_img_link\" : \"https://dummy-img-link.com\"\n    }]",
      "nullable": true
    },
    "mandate_data": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MandateData"
        }
      ],
      "nullable": true
    },
    "customer_acceptance": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CustomerAcceptance"
        }
      ],
      "nullable": true
    },
    "browser_info": {
      "allOf": [
        {
          "$ref": "#/components/schemas/BrowserInformation"
        }
      ],
      "nullable": true
    },
    "payment_experience": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentExperience"
        }
      ],
      "nullable": true
    },
    "payment_method_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentMethodType"
        }
      ],
      "nullable": true
    },
    "merchant_connector_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MerchantConnectorDetailsWrap"
        }
      ],
      "nullable": true
    },
    "allowed_payment_method_types": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/PaymentMethodType"
      },
      "description": "Use this parameter to restrict the Payment Method Types to show for a given PaymentIntent",
      "nullable": true
    },
    "retry_action": {
      "allOf": [
        {
          "$ref": "#/components/schemas/RetryAction"
        }
      ],
      "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
    },
    "connector_metadata": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ConnectorMetadata"
        }
      ],
      "nullable": true
    },
    "payment_link": {
      "type": "boolean",
      "description": "Whether to generate the payment link for this payment or not (if applicable)",
      "default": false,
      "example": true,
      "nullable": true
    },
    "payment_link_config": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentCreatePaymentLinkConfig"
        }
      ],
      "nullable": true
    },
    "payment_link_config_id": {
      "type": "string",
      "description": "Custom payment link config id set at business profile, send only if business_specific_configs is configured",
      "nullable": true
    },
    "surcharge_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/RequestSurchargeDetails"
        }
      ],
      "nullable": true
    },
    "payment_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentType"
        }
      ],
      "nullable": true
    },
    "request_incremental_authorization": {
      "type": "boolean",
      "description": "Request an incremental authorization, i.e., increase the authorized amount on a confirmed payment before you capture it.",
      "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
    },
    "frm_metadata": {
      "type": "object",
      "description": "Additional data related to some frm(Fraud Risk Management) connectors",
      "nullable": true
    },
    "request_external_three_ds_authentication": {
      "type": "boolean",
      "description": "Whether to perform external authentication (if applicable)",
      "example": true,
      "nullable": true
    },
    "recurring_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/RecurringDetails"
        }
      ],
      "nullable": true
    },
    "split_payments": {
      "allOf": [
        {
          "$ref": "#/components/schemas/SplitPaymentsRequest"
        }
      ],
      "nullable": true
    },
    "request_extended_authorization": {
      "type": "boolean",
      "description": "Optional boolean value to extent authorization period of this payment\n\ncapture method must be manual or manual_multiple",
      "default": false,
      "nullable": true
    },
    "merchant_order_reference_id": {
      "type": "string",
      "description": "Your unique identifier for this payment or order. This ID helps you reconcile payments on your system. If provided, it is passed to the connector if supported.",
      "example": "Custom_Order_id_123",
      "nullable": true,
      "maxLength": 255
    },
    "skip_external_tax_calculation": {
      "type": "boolean",
      "description": "Whether to calculate tax for this payment intent",
      "nullable": true
    },
    "psd2_sca_exemption_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ScaExemptionType"
        }
      ],
      "nullable": true
    },
    "ctp_service_details": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CtpServiceDetails"
        }
      ],
      "nullable": true
    },
    "force_3ds_challenge": {
      "type": "boolean",
      "description": "Indicates if 3ds challenge is forced",
      "nullable": true
    },
    "threeds_method_comp_ind": {
      "allOf": [
        {
          "$ref": "#/components/schemas/ThreeDsCompletionIndicator"
        }
      ],
      "nullable": true
    },
    "is_iframe_redirection_enabled": {
      "type": "boolean",
      "description": "Indicates if the redirection has to open in the iframe",
      "nullable": true
    },
    "all_keys_required": {
      "type": "boolean",
      "description": "If enabled, provides whole connector response",
      "nullable": true
    },
    "payment_channel": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PaymentChannel"
        }
      ],
      "nullable": true
    },
    "tax_status": {
      "allOf": [
        {
          "$ref": "#/components/schemas/TaxStatus"
        }
      ],
      "nullable": true
    },
    "discount_amount": {
      "type": "integer",
      "format": "int64",
      "description": "Total amount of the discount you have applied to the order or transaction.",
      "example": 6540,
      "nullable": true
    },
    "shipping_amount_tax": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MinorUnit"
        }
      ],
      "nullable": true
    },
    "duty_amount": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MinorUnit"
        }
      ],
      "nullable": true
    },
    "order_date": {
      "type": "string",
      "format": "date-time",
      "description": "Date the payer placed the order.",
      "nullable": true
    },
    "enable_partial_authorization": {
      "type": "boolean",
      "description": "Allow partial authorization for this payment",
      "nullable": true
    }
  }
}


pub fn is_elimination_enabled(&self) -> bool {
        self.elimination_routing_algorithm
            .as_ref()
            .map(|elimination_routing| {
                elimination_routing.enabled_feature
                    == DynamicRoutingFeatures::DynamicConnectorSelection
                    || elimination_routing.enabled_feature == DynamicRoutingFeatures::Metrics
            })
            .unwrap_or_default()
    }


File: crates/common_utils/src/lib.rs
 Public functions: 18
 Public structs: 4

#![warn(missing_docs, missing_debug_implementations)]
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR" ), "/", "README.md"))]

use masking::{PeekInterface, Secret};

pub mod access_token;
pub mod consts;
pub mod crypto;
pub mod custom_serde;
#[allow(missing_docs)] // Todo: add docs
pub mod encryption;
pub mod errors;
#[allow(missing_docs)] // Todo: add docs
pub mod events;
pub mod ext_traits;
pub mod fp_utils;
pub mod id_type;
#[cfg(feature = "keymanager")]
pub mod keymanager;
pub mod link_utils;
pub mod macros;
pub mod new_type;
pub mod payout_method_utils;
pub mod pii;
#[allow(missing_docs)] // Todo: add docs
pub mod request;
#[cfg(feature = "signals")]
pub mod signals;
pub mod transformers;
pub mod types;
pub mod validation;

/// Used for hashing
pub mod hashing;
#[cfg(feature = "metrics")]
pub mod metrics;

pub use base64_serializer::Base64Serializer;

/// Date-time utilities.
pub mod date_time {
    #[cfg(feature = "async_ext")]
    use std::time::Instant;
    use std::{marker::PhantomData, num::NonZeroU8};

    use masking::{Deserialize, Serialize};
    use time::{
        format_description::{
            well_known::iso8601::{Config, EncodedConfig, Iso8601, TimePrecision},
            BorrowedFormatItem,
        },
        OffsetDateTime, PrimitiveDateTime,
    };

    /// Enum to represent date formats
    #[derive(Debug)]
    pub enum DateFormat {
        /// Format the date in 20191105081132 format
        YYYYMMDDHHmmss,
        /// Format the date in 20191105 format
        YYYYMMDD,
        /// Format the date in 201911050811 format
        YYYYMMDDHHmm,
        /// Format the date in 05112019081132 format
        DDMMYYYYHHmmss,
    }

    /// Create a new [`PrimitiveDateTime`] with the current date and time in UTC.
    pub fn now() -> PrimitiveDateTime {
        let utc_date_time = OffsetDateTime::now_utc();
        PrimitiveDateTime::new(utc_date_time.date(), utc_date_time.time())
    }

    /// Convert from OffsetDateTime to PrimitiveDateTime
    pub fn convert_to_pdt(offset_time: OffsetDateTime) -> PrimitiveDateTime {
        PrimitiveDateTime::new(offset_time.date(), offset_time.time())
    }

    /// Return the UNIX timestamp of the current date and time in UTC
    pub fn now_unix_timestamp() -> i64 {
        OffsetDateTime::now_utc().unix_timestamp()
    }

    /// Calculate execution time for a async block in milliseconds
    #[cfg(feature = "async_ext")]
    pub async fn time_it<T, Fut: futures::Future<Output = T>, F: FnOnce() -> Fut>(
        block: F,
    ) -> (T, f64) {
        let start = Instant::now();
        let result = block().await;
        (result, start.elapsed().as_secs_f64() * 1000f64)
    }

    /// Return the given date and time in UTC with the given format Eg: format: YYYYMMDDHHmmss Eg: 20191105081132
    pub fn format_date(
        date: PrimitiveDateTime,
        format: DateFormat,
    ) -> Result<String, time::error::Format> {
        let format = <&[BorrowedFormatItem<'_>]>::from(format);
        date.format(&format)
    }

    /// Return the current date and time in UTC with the format [year]-[month]-[day]T[hour]:[minute]:[second].mmmZ Eg: 2023-02-15T13:33:18.898Z
    pub fn date_as_yyyymmddthhmmssmmmz() -> Result<String, time::error::Format> {
        const ISO_CONFIG: EncodedConfig = Config::DEFAULT
            .set_time_precision(TimePrecision::Second {
                decimal_digits: NonZeroU8::new(3),
            })
            .encode();
        now().assume_utc().format(&Iso8601::<ISO_CONFIG>)
    }

    /// Return the current date and time in UTC formatted as "ddd, DD MMM YYYY HH:mm:ss GMT".
    pub fn now_rfc7231_http_date() -> Result<String, time::error::Format> {
        let now_utc = OffsetDateTime::now_utc();
        // Desired format: ddd, DD MMM YYYY HH:mm:ss GMT
        // Example: Fri, 23 May 2025 06:19:35 GMT
        let format = time::macros::format_description!(
            "[weekday repr:short], [day padding:zero] [month repr:short] [year repr:full] [hour padding:zero repr:24]:[minute padding:zero]:[second padding:zero] GMT"
        );
        now_utc.format(&format)
    }

    impl From<DateFormat> for &[BorrowedFormatItem<'_>] {
        fn from(format: DateFormat) -> Self {
            match format {
                DateFormat::YYYYMMDDHHmmss => time::macros::format_description!("[year repr:full][month padding:zero repr:numerical][day padding:zero][hour padding:zero repr:24][minute padding:zero][second padding:zero]"),
                DateFormat::YYYYMMDD => time::macros::format_description!("[year repr:full][month padding:zero repr:numerical][day padding:zero]"),
                DateFormat::YYYYMMDDHHmm => time::macros::format_description!("[year repr:full][month padding:zero repr:numerical][day padding:zero][hour padding:zero repr:24][minute padding:zero]"),
                DateFormat::DDMMYYYYHHmmss => time::macros::format_description!("[day padding:zero][month padding:zero repr:numerical][year repr:full][hour padding:zero repr:24][minute padding:zero][second padding:zero]"),
            }
        }
    }

    /// Format the date in 05112019 format
    #[derive(Debug, Clone)]
    pub struct DDMMYYYY;
    /// Format the date in 20191105 format
    #[derive(Debug, Clone)]
    pub struct YYYYMMDD;
    /// Format the date in 20191105081132 format
    #[derive(Debug, Clone)]
    pub struct YYYYMMDDHHmmss;

    /// To serialize the date in Dateformats like YYYYMMDDHHmmss, YYYYMMDD, DDMMYYYY
    #[derive(Debug, Deserialize, Clone)]
    pub struct DateTime<T: TimeStrategy> {
        inner: PhantomData<T>,
        value: PrimitiveDateTime,
    }

    impl<T: TimeStrategy> From<PrimitiveDateTime> for DateTime<T> {
        fn from(value: PrimitiveDateTime) -> Self {
            Self {
                inner: PhantomData,
                value,
            }
        }
    }

    /// Time strategy for the Date, Eg: YYYYMMDDHHmmss, YYYYMMDD, DDMMYYYY
    pub trait TimeStrategy {
        /// Stringify the date as per the Time strategy
        fn fmt(input: &PrimitiveDateTime, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
    }

    impl<T: TimeStrategy> Serialize for DateTime<T> {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer,
        {
            serializer.collect_str(self)
        }
    }

    impl<T: TimeStrategy> std::fmt::Display for DateTime<T> {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            T::fmt(&self.value, f)
        }
    }

    impl TimeStrategy for DDMMYYYY {
        fn fmt(input: &PrimitiveDateTime, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            let year = input.year();
            #[allow(clippy::as_conversions)]
            let month = input.month() as u8;
            let day = input.day();
            let output = format!("{day:02}{month:02}{year}");
            f.write_str(&output)
        }
    }

    impl TimeStrategy for YYYYMMDD {
        fn fmt(input: &PrimitiveDateTime, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            let year = input.year();
            #[allow(clippy::as_conversions)]
            let month: u8 = input.month() as u8;
            let day = input.day();
            let output = format!("{year}{month:02}{day:02}");
            f.write_str(&output)
        }
    }

    impl TimeStrategy for YYYYMMDDHHmmss {
        fn fmt(input: &PrimitiveDateTime, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            let year = input.year();
            #[allow(clippy::as_conversions)]
            let month = input.month() as u8;
            let day = input.day();
            let hour = input.hour();
            let minute = input.minute();
            let second = input.second();
            let output = format!("{year}{month:02}{day:02}{hour:02}{minute:02}{second:02}");
            f.write_str(&output)
        }
    }
}

/// Generate a nanoid with the given prefix and length
#[inline]
pub fn generate_id(length: usize, prefix: &str) -> String {
    format!("{}_{}", prefix, nanoid::nanoid!(length, &consts::ALPHABETS))
}

/// Generate a ReferenceId with the default length with the given prefix
fn generate_ref_id_with_default_length<const MAX_LENGTH: u8, const MIN_LENGTH: u8>(
    prefix: &str,
) -> id_type::LengthId<MAX_LENGTH, MIN_LENGTH> {
    id_type::LengthId::<MAX_LENGTH, MIN_LENGTH>::new(prefix)
}

/// Generate a customer id with default length, with prefix as `cus`
pub fn generate_customer_id_of_default_length() -> id_type::CustomerId {
    use id_type::GenerateId;

    id_type::CustomerId::generate()
}

/// Generate a organization id with default length, with prefix as `org`
pub fn generate_organization_id_of_default_length() -> id_type::OrganizationId {
    use id_type::GenerateId;

    id_type::OrganizationId::generate()
}

/// Generate a profile id with default length, with prefix as `pro`
pub fn generate_profile_id_of_default_length() -> id_type::ProfileId {
    use id_type::GenerateId;

    id_type::ProfileId::generate()
}

/// Generate a routing id with default length, with prefix as `routing`
pub fn generate_routing_id_of_default_length() -> id_type::RoutingId {
    use id_type::GenerateId;

    id_type::RoutingId::generate()
}
/// Generate a merchant_connector_account id with default length, with prefix as `mca`
pub fn generate_merchant_connector_account_id_of_default_length(
) -> id_type::MerchantConnectorAccountId {
    use id_type::GenerateId;

    id_type::MerchantConnectorAccountId::generate()
}

/// Generate a profile_acquirer id with default length, with prefix as `mer_acq`
pub fn generate_profile_acquirer_id_of_default_length() -> id_type::ProfileAcquirerId {
    use id_type::GenerateId;

    id_type::ProfileAcquirerId::generate()
}

/// Generate a nanoid with the given prefix and a default length
#[inline]
pub fn generate_id_with_default_len(prefix: &str) -> String {
    let len: usize = consts::ID_LENGTH;
    format!("{}_{}", prefix, nanoid::nanoid!(len, &consts::ALPHABETS))
}

/// Generate a time-ordered (time-sortable) unique identifier using the current time
#[inline]
pub fn generate_time_ordered_id(prefix: &str) -> String {
    format!("{prefix}_{}", uuid::Uuid::now_v7().as_simple())
}

/// Generate a time-ordered (time-sortable) unique identifier using the current time without prefix
#[inline]
pub fn generate_time_ordered_id_without_prefix() -> String {
    uuid::Uuid::now_v7().as_simple().to_string()
}

/// Generate a nanoid with the specified length
#[inline]
pub fn generate_id_with_len(length: usize) -> String {
    nanoid::nanoid!(length, &consts::ALPHABETS)
}
#[allow(missing_docs)]
pub trait DbConnectionParams {
    fn get_username(&self) -> &str;
    fn get_password(&self) -> Secret<String>;
    fn get_host(&self) -> &str;
    fn get_port(&self) -> u16;
    fn get_dbname(&self) -> &str;
    fn get_database_url(&self, schema: &str) -> String {
        format!(
            "postgres://{}:{}@{}:{}/{}?application_name={}&options=-c search_path%3D{}",
            self.get_username(),
            self.get_password().peek(),
            self.get_host(),
            self.get_port(),
            self.get_dbname(),
            schema,
            schema,
        )
    }
}

// Can't add doc comments for macro invocations, neither does the macro allow it.
#[allow(missing_docs)]
mod base64_serializer {
    use base64_serde::base64_serde_type;

    base64_serde_type!(pub Base64Serializer, crate::consts::BASE64_ENGINE);
}

#[cfg(test)]
mod nanoid_tests {
    #![allow(clippy::unwrap_used)]
    use super::*;
    use crate::{
        consts::{
            MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH,
        },
        id_type::AlphaNumericId,
    };

    #[test]
    fn test_generate_id_with_alphanumeric_id() {
        let alphanumeric_id = AlphaNumericId::from(generate_id(10, "def").into());
        assert!(alphanumeric_id.is_ok())
    }

    #[test]
    fn test_generate_merchant_ref_id_with_default_length() {
        let ref_id = id_type::LengthId::<
            MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH,
            MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH,
        >::from(generate_id_with_default_len("def").into());

        assert!(ref_id.is_ok())
    }
}

/// Module for tokenization-related functionality
///
/// This module provides types and functions for handling tokenized payment data,
/// including response structures and token generation utilities.
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
pub mod tokenization;


pub fn get_topic(&self, event: EventType) -> &str {
        match event {
            EventType::FraudCheck => &self.fraud_check_analytics_topic,
            EventType::ApiLogs => &self.api_logs_topic,
            EventType::PaymentAttempt => &self.attempt_analytics_topic,
            EventType::PaymentIntent => &self.intent_analytics_topic,
            EventType::Refund => &self.refund_analytics_topic,
            EventType::ConnectorApiLogs => &self.connector_logs_topic,
            EventType::OutgoingWebhookLogs => &self.outgoing_webhook_logs_topic,
            EventType::Dispute => &self.dispute_analytics_topic,
            EventType::AuditEvent => &self.audit_events_topic,
            #[cfg(feature = "payouts")]
            EventType::Payout => &self.payout_analytics_topic,
            EventType::Consolidated => &self.consolidated_events_topic,
            EventType::Authentication => &self.authentication_analytics_topic,
            EventType::RoutingApiLogs => &self.routing_logs_topic,
            EventType::RevenueRecovery => &self.revenue_recovery_topic,
        }
    }


pub struct KafkaPaymentIntentEvent<'a> {
    pub payment_id: &'a id_type::PaymentId,
    pub merchant_id: &'a id_type::MerchantId,
    pub status: storage_enums::IntentStatus,
    pub amount: common_types::MinorUnit,
    pub currency: Option<storage_enums::Currency>,
    pub amount_captured: Option<common_types::MinorUnit>,
    pub customer_id: Option<&'a id_type::CustomerId>,
    pub description: Option<&'a String>,
    pub return_url: Option<&'a String>,
    pub metadata: Option<String>,
    pub connector_id: Option<&'a String>,
    pub statement_descriptor_name: Option<&'a String>,
    pub statement_descriptor_suffix: Option<&'a String>,
    #[serde(with = "time::serde::timestamp::nanoseconds")]
    pub created_at: OffsetDateTime,
    #[serde(with = "time::serde::timestamp::nanoseconds")]
    pub modified_at: OffsetDateTime,
    #[serde(default, with = "time::serde::timestamp::nanoseconds::option")]
    pub last_synced: Option<OffsetDateTime>,
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    pub off_session: Option<bool>,
    pub client_secret: Option<&'a String>,
    pub active_attempt_id: String,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<&'a String>,
    pub attempt_count: i16,
    pub profile_id: Option<&'a id_type::ProfileId>,
    pub payment_confirm_source: Option<storage_enums::PaymentSource>,
    pub billing_details: Option<Encryptable<Secret<Value>>>,
    pub shipping_details: Option<Encryptable<Secret<Value>>>,
    pub customer_email: Option<HashedString<pii::EmailStrategy>>,
    pub feature_metadata: Option<&'a Value>,
    pub merchant_order_reference_id: Option<&'a String>,
    pub organization_id: &'a id_type::OrganizationId,
    #[serde(flatten)]
    pub infra_values: Option<Value>,
}


pub struct MetricsBucketResponse {
    #[serde(flatten)]
    pub values: ActivePaymentsMetricsBucketValue,
    #[serde(flatten)]
    pub dimensions: ActivePaymentsMetricsBucketIdentifier,
}


pub struct HipayBrowserInfo {
    java_enabled: Option<bool>,
    javascript_enabled: Option<bool>,
    ipaddr: Option<std::net::IpAddr>,
    http_accept: String,
    http_user_agent: Option<String>,
    language: Option<String>,
    color_depth: Option<u8>,
    screen_height: Option<u32>,
    screen_width: Option<u32>,
    timezone: Option<i32>,
}