Documentation: config/deployments/README.md
# Type: Doc File

# Configs for deployments

## Introduction

This directory contains the configs for deployments of Hyperswitch in different hosted environments.

Hyperswitch has **3** components namely,

- router
- drainer
- scheduler
  - consumer
  - producer

We maintain configs for the `router` component for 3 different environments, namely,

- Integration Test
- Sandbox
- Production

To learn about what "router", "drainer" and "scheduler" is, please refer to the [Hyperswitch architecture][architecture] documentation.

### Tree structure

```text
config/deployments            # Root directory for the deployment configs
├── README.md                 # This file
├── drainer.toml              # Config specific to drainer
├── env_specific.toml         # Config for environment specific values which are meant to be sensitive (to be set by the user)
├── integration_test.toml     # Config specific to integration_test environment
├── production.toml           # Config specific to production environment
├── sandbox.toml              # Config specific to sandbox environment
└── scheduler                 # Directory for scheduler configs
    ├── consumer.toml         # Config specific to consumer
    └── producer.toml         # Config specific to producer
```

## Router

The `integration_test.toml`, `sandbox.toml`, and `production.toml` files are configuration files for the environments `integration_test`, `sandbox`, and `production`, respectively. These files maintain a 1:1 mapping with the environment names, and it is recommended to use the same name for the environment throughout this document.

### Generating a Config File for the Router

The `env_specific.toml` file contains values that are specific to the environment. This file is kept separate because the values in it are sensitive and are meant to be set by the user. The `env_specific.toml` file is merged with the `integration_test.toml`, `sandbox.toml`, or `production.toml` file to create the final configuration file for the router.

For example, to build and deploy Hyperswitch in the **sandbox environment**, you can duplicate the `env_specific.toml` file and rename it as `sandbox_config.toml`. Then, update the values in the file with the proper values for the sandbox environment.

The environment-specific `sandbox.toml` file, which contains the Hyperswitch recommended defaults, is merged with the `sandbox_config.toml` file to create the final configuration file called `sandbox_release.toml`. This file is marked as ready for deploying on the sandbox environment.

1. Duplicate the `env_specific.toml` file and rename it as `sandbox_config.toml`:

   ```shell
   cp config/deployments/env_specific.toml config/deployments/sandbox_config.toml
   ```

2. Update the values in the `sandbox_config.toml` file with the proper values for the sandbox environment:

   ```shell
   vi config/deployments/sandbox_config.toml
   ```

3. To merge the files you can use `cat`:

   ```shell
   cat config/deployments/sandbox.toml config/deployments/sandbox_config.toml > config/deployments/sandbox_release.toml
   ```

> [!NOTE]
> You can refer to the [`config.example.toml`][config_example] file to understand the variables that used are in the `env_specific.toml` file.

## Scheduler

The scheduler has two components, namely `consumer` and `producer`.

The `consumer.toml` and `producer.toml` files are the configuration files for the `consumer` and `producer`, respectively. These files contain the default values recommended by Hyperswitch.

### Generating a Config File for the Scheduler

Scheduler configuration files are built on top of the router configuration files. So, the `sandbox_release.toml` file is merged with the `consumer.toml` or `producer.toml` file to create the final configuration file for the scheduler.

You can use `cat` to merge the files in the terminal.

- Below is an example for consumer in sandbox environment:

  ```shell
  cat config/deployments/scheduler/consumer.toml config/deployments/sandbox_release.toml > config/deployments/consumer_sandbox_release.toml
  ```

- Below is an example for producer in sandbox environment:

  ```shell
  cat config/deployments/scheduler/producer.toml config/deployments/sandbox_release.toml > config/deployments/producer_sandbox_release.toml
  ```

## Drainer

Drainer is an independent component, and hence, the drainer configs can be used directly provided that the user updates the `drainer.toml` file with proper values before using.

## Running Hyperswitch through Docker Compose

To run the router, you can use the following snippet in the `docker-compose.yml` file:

```yaml
### Application services
hyperswitch-server:
  image: docker.juspay.io/juspaydotin/hyperswitch-router:latest # This pulls the latest image from Docker Hub. If you wish to use a version without added features (like KMS), you can replace `latest` with `standalone`. However, please note that the standalone version is not recommended for production use.
  command: /local/bin/router --config-path /local/config/deployments/sandbox_release.toml # <--- Change this to the config file that is generated for the environment.
  ports:
    - "8080:8080"
  volumes:
    - ./config:/local/config
```

To run the producer, you can use the following snippet in the `docker-compose.yml` file:

```yaml
hyperswitch-producer:
  image: docker.juspay.io/juspaydotin/hyperswitch-producer:latest
  command: /local/bin/scheduler --config-path /local/config/deployments/producer_sandbox_release.toml # <--- Change this to the config file that is generated for the environment.
  volumes:
    - ./config:/local/config
  environment:
    - SCHEDULER_FLOW=producer
```

To run the consumer, you can use the following snippet in the `docker-compose.yml` file:

```yaml
hyperswitch-consumer:
  image: docker.juspay.io/juspaydotin/hyperswitch-consumer:latest
  command: /local/bin/scheduler --config-path /local/config/deployments/consumer_sandbox_release.toml # <--- Change this to the config file that is generated for the environment
  volumes:
    - ./config:/local/config
  environment:
    - SCHEDULER_FLOW=consumer
```

To run the drainer, you can use the following snippet in the `docker-compose.yml` file:

```yaml
hyperswitch-drainer:
  image: docker.juspay.io/juspaydotin/hyperswitch-drainer:latest
  command: /local/bin/drainer --config-path /local/config/deployments/drainer.toml
  volumes:
    - ./config:/local/config
```

> [!NOTE]
> You can replace the term `sandbox` with the environment name that you are deploying to (e.g., `production`, `integration_test`, etc.) with respective changes (optional) and use the same steps to generate the final configuration file for the environment.

You can verify that the server is up and running by hitting the health check endpoint:

```shell
curl --head --request GET 'http://localhost:8080/health'
```

[architecture]: /docs/architecture.md
[config_example]: /config/config.example.toml


pub struct JpmorganCancelResponse {
    transaction_id: String,
    request_id: String,
    response_status: JpmorganResponseStatus,
    response_code: String,
    response_message: String,
    payment_method_type: JpmorganPaymentMethodTypeCancelResponse,
}


pub async fn get_sankey_data(
    clickhouse_client: &ClickhouseClient,
    auth: &AuthInfo,
    time_range: &TimeRange,
) -> MetricsResult<Vec<SankeyRow>> {
    let mut query_builder =
        QueryBuilder::<ClickhouseClient>::new(AnalyticsCollection::Authentications);

    query_builder
        .add_select_column(Aggregate::<String>::Count {
            field: None,
            alias: Some("count"),
        })
        .change_context(MetricsError::QueryBuildingError)?;

    query_builder
        .add_select_column("exemption_requested")
        .change_context(MetricsError::QueryBuildingError)?;

    query_builder
        .add_select_column("exemption_accepted")
        .change_context(MetricsError::QueryBuildingError)?;

    query_builder
        .add_select_column("authentication_status")
        .change_context(MetricsError::QueryBuildingError)?;

    auth.set_filter_clause(&mut query_builder)
        .change_context(MetricsError::QueryBuildingError)?;

    time_range
        .set_filter_clause(&mut query_builder)
        .change_context(MetricsError::QueryBuildingError)?;

    query_builder
        .add_group_by_clause("exemption_requested")
        .change_context(MetricsError::QueryBuildingError)?;

    query_builder
        .add_group_by_clause("exemption_accepted")
        .change_context(MetricsError::QueryBuildingError)?;

    query_builder
        .add_group_by_clause("authentication_status")
        .change_context(MetricsError::QueryBuildingError)?;

    query_builder
        .execute_query::<SankeyRow, _>(clickhouse_client)
        .await
        .change_context(MetricsError::QueryBuildingError)?
        .change_context(MetricsError::QueryExecutionFailure)?
        .into_iter()
        .map(Ok)
        .collect()
}


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


pub struct CalGlobalSuccessRateEventRequest {
    pub entity_id: String,
    pub entity_params: String,
    pub entity_labels: Vec<String>,
    pub global_labels: Vec<String>,
    pub config: Option<CalGlobalSuccessRateConfigEventRequest>,
}


pub struct ZenCustomerDetails {
    email: pii::Email,
    ip: Secret<String, pii::IpAddress>,
}


impl ConnectorValidation for Gocardless {
    fn validate_mandate_payment(
        &self,
        pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        let mandate_supported_pmd = std::collections::HashSet::from([
            PaymentMethodDataType::SepaBankDebit,
            PaymentMethodDataType::AchBankDebit,
            PaymentMethodDataType::BecsBankDebit,
            PaymentMethodDataType::BacsBankDebit,
        ]);
        is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id())
    }
}

impl ConnectorValidation for for Gocardless


impl Health {
    pub fn server(state: routes::AppState, service: String) -> Scope {
        web::scope("health")
            .app_data(web::Data::new(state))
            .app_data(web::Data::new(service))
            .service(web::resource("").route(web::get().to(health)))
            .service(web::resource("/ready").route(web::get().to(deep_health_check)))
    }
}


impl AuthEventMetricAccumulator for CountAccumulator {
    type MetricOutput = Option<u64>;
    #[inline]
    fn add_metrics_bucket(&mut self, metrics: &AuthEventMetricRow) {
        self.count = match (self.count, metrics.count) {
            (None, None) => None,
            (None, i @ Some(_)) | (i @ Some(_), None) => i,
            (Some(a), Some(b)) => Some(a + b),
        }
    }
    #[inline]
    fn collect(self) -> Self::MetricOutput {
        self.count.and_then(|i| u64::try_from(i).ok())
    }
}

impl AuthEventMetricAccumulator for for CountAccumulator


Documentation: api-reference/v1/payouts/payouts--filter.mdx
# Type: Doc File

---
openapi: post /payouts/list
---


pub async fn payment_method_update_api() {}


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

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


impl Responder {
    let flow = Flow::ApiKeyUpdate;
    let api_key_id = key_id.into_inner();
    let mut payload = json_payload.into_inner();
    payload.key_id = api_key_id;

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state,
         auth::AuthenticationDataWithoutProfile {
             merchant_account, ..
         },
         mut payload,
         _| {
            payload.merchant_id = merchant_account.get_id().to_owned();
            api_keys::update_api_key(state, payload)
        },
        auth::auth_type(
            &auth::AdminApiAuthWithMerchantIdFromHeader,
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantApiKeyRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct StaxChildCapture {
    id: String,
}


File: crates/diesel_models/src/query/connector_response.rs
 Public functions: 3

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};
use router_env::{instrument, logger, tracing};

use super::generics;
use crate::{
    connector_response::{
        ConnectorResponse, ConnectorResponseNew, ConnectorResponseUpdate,
        ConnectorResponseUpdateInternal,
    },
    errors,
    payment_attempt::{PaymentAttempt, PaymentAttemptUpdate, PaymentAttemptUpdateInternal},
    schema::{connector_response::dsl, payment_attempt::dsl as pa_dsl},
    PgPooledConn, StorageResult,
};

impl ConnectorResponseNew {
    #[instrument(skip(conn))]
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<ConnectorResponse> {
        let payment_attempt_update = PaymentAttemptUpdate::ConnectorResponse {
            authentication_data: self.authentication_data.clone(),
            encoded_data: self.encoded_data.clone(),
            connector_transaction_id: self.connector_transaction_id.clone(),
            connector: self.connector_name.clone(),
            updated_by: self.updated_by.clone(),
            charge_id: self.charge_id.clone(),
        };

        let _payment_attempt: Result<PaymentAttempt, _> =
            generics::generic_update_with_unique_predicate_get_result::<
                <PaymentAttempt as HasTable>::Table,
                _,
                _,
                _,
            >(
                conn,
                pa_dsl::attempt_id
                    .eq(self.attempt_id.to_owned())
                    .and(pa_dsl::merchant_id.eq(self.merchant_id.to_owned())),
                PaymentAttemptUpdateInternal::from(payment_attempt_update),
            )
            .await
            .inspect_err(|err| {
                logger::error!(
                    "Error while updating payment attempt in connector_response flow {:?}",
                    err
                );
            });

        generics::generic_insert(conn, self).await
    }
}

impl ConnectorResponse {
    #[instrument(skip(conn))]
    pub async fn update(
        self,
        conn: &PgPooledConn,
        connector_response: ConnectorResponseUpdate,
    ) -> StorageResult<Self> {
        let payment_attempt_update = match connector_response.clone() {
            ConnectorResponseUpdate::ResponseUpdate {
                connector_transaction_id,
                authentication_data,
                encoded_data,
                connector_name,
                charge_id,
                updated_by,
            } => PaymentAttemptUpdate::ConnectorResponse {
                authentication_data,
                encoded_data,
                connector_transaction_id,
                connector: connector_name,
                charge_id,
                updated_by,
            },
            ConnectorResponseUpdate::ErrorUpdate {
                connector_name,
                updated_by,
            } => PaymentAttemptUpdate::ConnectorResponse {
                authentication_data: None,
                encoded_data: None,
                connector_transaction_id: None,
                connector: connector_name,
                charge_id: None,
                updated_by,
            },
        };

        let _payment_attempt: Result<PaymentAttempt, _> =
            generics::generic_update_with_unique_predicate_get_result::<
                <PaymentAttempt as HasTable>::Table,
                _,
                _,
                _,
            >(
                conn,
                pa_dsl::attempt_id
                    .eq(self.attempt_id.to_owned())
                    .and(pa_dsl::merchant_id.eq(self.merchant_id.to_owned())),
                PaymentAttemptUpdateInternal::from(payment_attempt_update),
            )
            .await
            .inspect_err(|err| {
                logger::error!(
                    "Error while updating payment attempt in connector_response flow {:?}",
                    err
                );
            });

        let connector_response_result =
            match generics::generic_update_with_unique_predicate_get_result::<
                <Self as HasTable>::Table,
                _,
                _,
                _,
            >(
                conn,
                dsl::merchant_id
                    .eq(self.merchant_id.clone())
                    .and(dsl::payment_id.eq(self.payment_id.clone()))
                    .and(dsl::attempt_id.eq(self.attempt_id.clone())),
                ConnectorResponseUpdateInternal::from(connector_response),
            )
            .await
            {
                Err(error) => match error.current_context() {
                    errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                    _ => Err(error),
                },
                result => result,
            };

        connector_response_result
    }

    #[instrument(skip(conn))]
    pub async fn find_by_payment_id_merchant_id_attempt_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        attempt_id: &str,
    ) -> StorageResult<Self> {
        let connector_response: Self =
            generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
                conn,
                dsl::merchant_id.eq(merchant_id.to_owned()).and(
                    dsl::payment_id
                        .eq(payment_id.to_owned())
                        .and(dsl::attempt_id.eq(attempt_id.to_owned())),
                ),
            )
            .await?;

        match generics::generic_find_one::<<PaymentAttempt as HasTable>::Table, _, _>(
            conn,
            pa_dsl::payment_id.eq(payment_id.to_owned()).and(
                pa_dsl::merchant_id
                    .eq(merchant_id.to_owned())
                    .and(pa_dsl::attempt_id.eq(attempt_id.to_owned())),
            ),
        )
        .await
        {
            Ok::<PaymentAttempt, _>(payment_attempt) => {
                if payment_attempt.authentication_data != connector_response.authentication_data {
                    logger::error!(
                        "Not Equal pa_authentication_data : {:?}, cr_authentication_data: {:?} ",
                        payment_attempt.authentication_data,
                        connector_response.authentication_data
                    );
                }

                if payment_attempt.encoded_data != connector_response.encoded_data {
                    logger::error!(
                        "Not Equal pa_encoded_data : {:?}, cr_encoded_data: {:?} ",
                        payment_attempt.encoded_data,
                        connector_response.encoded_data
                    );
                }

                if payment_attempt.connector_transaction_id
                    != connector_response.connector_transaction_id
                {
                    logger::error!(
                            "Not Equal pa_connector_transaction_id : {:?}, cr_connector_transaction_id: {:?} ",
                            payment_attempt.connector_transaction_id,
                            connector_response.connector_transaction_id
                        );
                }
                if payment_attempt.connector != connector_response.connector_name {
                    logger::error!(
                        "Not Equal pa_connector : {:?}, cr_connector_name: {:?} ",
                        payment_attempt.connector,
                        connector_response.connector_name
                    );
                }
            }
            Err(err) => {
                logger::error!(
                    "Error while finding payment attempt in connector_response flow {:?}",
                    err
                );
            }
        }

        Ok(connector_response)
    }
}


OpenAPI Block Path: components.schemas.RevolutPayData
{
  "type": "object"
}


pub struct GetCard<'a> {
    merchant_id: &'a str,
    card_id: &'a str,
}


pub struct CloneConnectorDestination {
    pub connector_label: Option<String>,
    pub profile_id: id_type::ProfileId,
    pub merchant_id: id_type::MerchantId,
}


impl PaymentMethodsEnabled {
    /// Get payment_method_type
    #[cfg(feature = "v2")]
    pub fn get_payment_method(&self) -> Option<common_enums::PaymentMethod> {
        Some(self.payment_method_type)
    }

    /// Get payment_method_subtypes
    #[cfg(feature = "v2")]
    pub fn get_payment_method_type(&self) -> Option<&Vec<RequestPaymentMethodTypes>> {
        self.payment_method_subtypes.as_ref()
    }
}


pub fn new(
        token: auth::UserFromSinglePurposeToken,
        current_flow: UserFlow,
    ) -> UserResult<Self> {
        let flows = token.origin.get_flows();
        let index = flows
            .iter()
            .position(|flow| flow == &current_flow)
            .ok_or(UserErrors::InternalServerError)?;
        let mut path = token.path;
        path.push(current_flow.into());

        Ok(Self {
            origin: token.origin,
            current_flow_index: index,
            path,
            tenant_id: token.tenant_id,
        })
    }


pub async fn increment_blocked_count_in_cache(
    state: &SessionState,
    card_testing_guard_data: Option<CardTestingGuardData>,
) -> RouterResult<()> {
    if let Some(card_testing_guard_data) = card_testing_guard_data.clone() {
        if card_testing_guard_data.is_card_ip_blocking_enabled
            && !card_testing_guard_data
                .card_ip_blocking_cache_key
                .is_empty()
        {
            let _ = services::card_testing_guard::increment_blocked_count_in_cache(
                state,
                &card_testing_guard_data.card_ip_blocking_cache_key,
                card_testing_guard_data.card_testing_guard_expiry.into(),
            )
            .await;
        }

        if card_testing_guard_data.is_guest_user_card_blocking_enabled
            && !card_testing_guard_data
                .guest_user_card_blocking_cache_key
                .is_empty()
        {
            let _ = services::card_testing_guard::increment_blocked_count_in_cache(
                state,
                &card_testing_guard_data.guest_user_card_blocking_cache_key,
                card_testing_guard_data.card_testing_guard_expiry.into(),
            )
            .await;
        }

        if card_testing_guard_data.is_customer_id_blocking_enabled
            && !card_testing_guard_data
                .customer_id_blocking_cache_key
                .is_empty()
        {
            let _ = services::card_testing_guard::increment_blocked_count_in_cache(
                state,
                &card_testing_guard_data.customer_id_blocking_cache_key,
                card_testing_guard_data.card_testing_guard_expiry.into(),
            )
            .await;
        }
    }
    Ok(())
}


#[cfg(feature = "v2")]
//List Payment Method
#[derive(Debug, Clone, serde::Serialize, Default, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct ListMethodsForPaymentMethodsRequest {
    /// This is a 15 minute expiry token which shall be used from the client to authenticate and perform sessions from the SDK
    #[schema(max_length = 30, min_length = 30, example = "secret_k2uj3he2893eiu2d")]
    pub client_secret: Option<String>,

    /// The two-letter ISO currency code
    #[schema(value_type = Option<Vec<CountryAlpha2>>, example = json!(["US", "UK", "IN"]))]
    pub accepted_countries: Option<Vec<api_enums::CountryAlpha2>>,

    /// Filter by amount
    #[schema(example = 60)]
    pub amount: Option<MinorUnit>,

    /// The three-letter ISO currency code
    #[schema(value_type = Option<Vec<Currency>>,example = json!(["USD", "EUR"]))]
    pub accepted_currencies: Option<Vec<api_enums::Currency>>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// Indicates whether the payment method is eligible for card netwotks
    #[schema(value_type = Option<Vec<CardNetwork>>, example = json!(["visa", "mastercard"]))]
    pub card_networks: Option<Vec<api_enums::CardNetwork>>,

    /// Indicates the limit of last used payment methods
    #[schema(example = 1)]
    pub limit: Option<i64>,
}

#[cfg(feature = "v2")]
impl<'de> serde::Deserialize<'de> for ListMethodsForPaymentMethodsRequest {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct FieldVisitor;

        impl<'de> de::Visitor<'de> for FieldVisitor {
            type Value = ListMethodsForPaymentMethodsRequest;

            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                formatter.write_str("Failed while deserializing as map")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
            where
                A: de::MapAccess<'de>,
            {
                let mut output = ListMethodsForPaymentMethodsRequest::default();

                while let Some(key) = map.next_key()? {
                    match key {
                        "client_secret" => {
                            set_or_reject_duplicate(
                                &mut output.client_secret,
                                "client_secret",
                                map.next_value()?,
                            )?;
                        }
                        "accepted_countries" => match output.accepted_countries.as_mut() {
                            Some(inner) => inner.push(map.next_value()?),
                            None => {
                                output.accepted_countries = Some(vec![map.next_value()?]);
                            }
                        },
                        "amount" => {
                            set_or_reject_duplicate(
                                &mut output.amount,
                                "amount",
                                map.next_value()?,
                            )?;
                        }
                        "accepted_currencies" => match output.accepted_currencies.as_mut() {
                            Some(inner) => inner.push(map.next_value()?),
                            None => {
                                output.accepted_currencies = Some(vec![map.next_value()?]);
                            }
                        },
                        "recurring_enabled" => {
                            set_or_reject_duplicate(
                                &mut output.recurring_enabled,
                                "recurring_enabled",
                                map.next_value()?,
                            )?;
                        }
                        "card_network" => match output.card_networks.as_mut() {
                            Some(inner) => inner.push(map.next_value()?),
                            None => output.card_networks = Some(vec![map.next_value()?]),
                        },
                        "limit" => {
                            set_or_reject_duplicate(&mut output.limit, "limit", map.next_value()?)?;
                        }
                        _ => {}
                    }
                }

                Ok(output)
            }
        }

        deserializer.deserialize_identifier(FieldVisitor)
    }
}

// Try to set the provided value to the data otherwise throw an error
fn set_or_reject_duplicate<T, E: de::Error>(
    data: &mut Option<T>,
    name: &'static str,
    value: T,
) -> Result<(), E> {
    match data {
        Some(_inner) => Err(de::Error::duplicate_field(name)),
        None => {
            *data = Some(value);
            Ok(())
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodListResponse {
    /// Redirect URL of the merchant
    #[schema(example = "https://www.google.com")]
    pub redirect_url: Option<String>,

    /// currency of the Payment to be done
    #[schema(example = "USD", value_type = Currency)]
    pub currency: Option<api_enums::Currency>,

    /// Information about the payment method
    pub payment_methods: Vec<ResponsePaymentMethodsEnabled>,
    /// Value indicating if the current payment is a mandate payment
    #[schema(value_type = MandateType)]
    pub mandate_payment: Option<payments::MandateType>,

    #[schema(value_type = Option<String>)]
    pub merchant_name: OptionalEncryptableName,

    /// flag to indicate if surcharge and tax breakup screen should be shown or not
    #[schema(value_type = bool)]
    pub show_surcharge_breakup_screen: bool,

    #[schema(value_type = Option<PaymentType>)]
    pub payment_type: Option<api_enums::PaymentType>,

    /// flag to indicate whether to perform external 3ds authentication
    #[schema(example = true)]
    pub request_external_three_ds_authentication: bool,

    /// flag that indicates whether to collect shipping details from wallets or from the customer
    pub collect_shipping_details_from_wallets: Option<bool>,

    /// flag that indicates whether to collect billing details from wallets or from the customer
    pub collect_billing_details_from_wallets: Option<bool>,

    /// flag that indicates whether to calculate tax on the order amount
    pub is_tax_calculation_enabled: bool,
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethodsListResponse {
    /// List of payment methods for customer
    pub customer_payment_methods: Vec<CustomerPaymentMethod>,
    /// Returns whether a customer id is not tied to a payment intent (only when the request is made against a client secret)
    pub is_guest_customer: Option<bool>,
}

// OLAP PML Response
#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethodsListResponse {
    /// List of payment methods for customer
    pub customer_payment_methods: Vec<PaymentMethodResponseItem>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct GetTokenDataRequest {
    /// Indicates the type of token to be fetched
    pub token_type: api_enums::TokenDataType,
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for GetTokenDataRequest {}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct TokenDataResponse {
    /// The unique identifier of the payment method.
    #[schema(value_type = String, example = "12345_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub payment_method_id: id_type::GlobalPaymentMethodId,

    /// token type of the payment method
    #[schema(value_type = TokenDataType)]
    pub token_type: api_enums::TokenDataType,

    /// token details of the payment method
    pub token_details: TokenDetailsResponse,
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for TokenDataResponse {}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
#[serde(untagged)]
pub enum TokenDetailsResponse {
    NetworkTokenDetails(NetworkTokenDetailsResponse),
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct NetworkTokenDetailsResponse {
    /// Network token generated against the Card Number
    #[schema(value_type = String)]
    pub network_token: cards::NetworkToken,

    /// Expiry month of the network token
    #[schema(value_type = String)]
    pub network_token_exp_month: masking::Secret<String>,

    /// Expiry year of the network token
    #[schema(value_type = String)]
    pub network_token_exp_year: masking::Secret<String>,

    /// Cryptogram generated by the Network
    #[schema(value_type = Option<String>)]
    pub cryptogram: Option<masking::Secret<String>>,

    /// Issuer of the card
    pub card_issuer: Option<String>,

    /// Card network of the token
    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<common_enums::CardNetwork>,

    /// Card type of the token
    pub card_type: Option<CardType>,

    /// Issuing country of the card
    #[schema(value_type = Option<CountryAlpha2>)]
    pub card_issuing_country: Option<common_enums::CountryAlpha2>,

    /// Bank code of the card
    pub bank_code: Option<String>,

    /// Name of the card holder
    #[schema(value_type = Option<String>)]
    pub card_holder_name: Option<masking::Secret<String>>,

    /// Nick name of the card holder
    #[schema(value_type = Option<String>)]
    pub nick_name: Option<masking::Secret<String>>,

    /// ECI indicator of the card
    pub eci: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct TotalPaymentMethodCountResponse {
    /// total count of payment methods under the merchant
    pub total_count: i64,
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for TotalPaymentMethodCountResponse {}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodDeleteResponse {
    /// The unique identifier of the Payment method
    #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")]
    pub payment_method_id: String,

    /// Whether payment method was deleted or not
    #[schema(example = true)]
    pub deleted: bool,
}

#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentMethodDeleteResponse {
    /// The unique identifier of the Payment method
    #[schema(value_type = String, example = "12345_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub id: id_type::GlobalPaymentMethodId,
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct CustomerDefaultPaymentMethodResponse {
    /// The unique identifier of the Payment method
    #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")]
    pub default_payment_method_id: Option<String>,
    /// The unique identifier of the customer.
    #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: id_type::CustomerId,
    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method: api_enums::PaymentMethod,
    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>,example = "credit")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct PaymentMethodResponseItem {
    /// The unique identifier of the payment method.
    #[schema(value_type = String, example = "12345_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub id: id_type::GlobalPaymentMethodId,

    /// The unique identifier of the customer.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "12345_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: id_type::GlobalCustomerId,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// This is a sub-category of payment method.
    #[schema(value_type = PaymentMethodType,example = "credit")]
    pub payment_method_subtype: api_enums::PaymentMethodType,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// PaymentMethod Data from locker
    pub payment_method_data: Option<PaymentMethodListData>,

    /// Masked bank details from PM auth services
    #[schema(example = json!({"mask": "0000"}))]
    pub bank: Option<MaskedBankDetails>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = PrimitiveDateTime, example = "2023-01-18T11:04:09.922Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created: time::PrimitiveDateTime,

    /// Whether this payment method requires CVV to be collected
    #[schema(example = true)]
    pub requires_cvv: bool,

    ///  A timestamp (ISO 8601 code) that determines when the payment method was last used
    #[schema(value_type = PrimitiveDateTime,example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601")]
    pub last_used_at: time::PrimitiveDateTime,

    /// Indicates if the payment method has been set to default or not
    #[schema(example = true)]
    pub is_default: bool,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,

    ///The network token details for the payment method
    pub network_tokenization: Option<NetworkTokenResponse>,

    /// Whether psp_tokenization is enabled for the payment_method, this will be true when at least
    /// one multi-use token with status `Active` is available for the payment method
    pub psp_tokenization_enabled: bool,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethodResponseItem {
    /// The unique identifier of the payment method.
    #[schema(value_type = String, example = "12345_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub id: id_type::GlobalPaymentMethodId,

    /// Temporary Token for payment method in vault which gets refreshed for every payment
    #[schema(example = "7ebf443f-a050-4067-84e5-e6f6d4800aef")]
    pub payment_token: String,

    /// The unique identifier of the customer.
    #[schema(
        min_length = 32,
        max_length = 64,
        example = "12345_cus_01926c58bc6e77c09e809964e72af8c8",
        value_type = String
    )]
    pub customer_id: id_type::GlobalCustomerId,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method_type: api_enums::PaymentMethod,

    /// This is a sub-category of payment method.
    #[schema(value_type = PaymentMethodType,example = "credit")]
    pub payment_method_subtype: api_enums::PaymentMethodType,

    /// Indicates whether the payment method is eligible for recurring payments
    #[schema(example = true)]
    pub recurring_enabled: bool,

    /// PaymentMethod Data from locker
    pub payment_method_data: Option<PaymentMethodListData>,

    /// Masked bank details from PM auth services
    #[schema(example = json!({"mask": "0000"}))]
    pub bank: Option<MaskedBankDetails>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = PrimitiveDateTime, example = "2023-01-18T11:04:09.922Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created: time::PrimitiveDateTime,

    /// Whether this payment method requires CVV to be collected
    #[schema(example = true)]
    pub requires_cvv: bool,

    ///  A timestamp (ISO 8601 code) that determines when the payment method was last used
    #[schema(value_type = PrimitiveDateTime,example = "2024-02-24T11:04:09.922Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub last_used_at: time::PrimitiveDateTime,

    /// Indicates if the payment method has been set to default or not
    #[schema(example = true)]
    pub is_default: bool,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum PaymentMethodListData {
    Card(CardDetailFromLocker),
    #[cfg(feature = "payouts")]
    #[schema(value_type = Bank)]
    Bank(payouts::Bank),
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
pub struct CustomerPaymentMethod {
    /// Token for payment method in temporary card locker which gets refreshed often
    #[schema(example = "7ebf443f-a050-4067-84e5-e6f6d4800aef")]
    pub payment_token: String,
    /// The unique identifier of the customer.
    #[schema(example = "pm_iouuy468iyuowqs")]
    pub payment_method_id: String,

    /// The unique identifier of the customer.
    #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: id_type::CustomerId,

    /// The type of payment method use for the payment.
    #[schema(value_type = PaymentMethod,example = "card")]
    pub payment_method: api_enums::PaymentMethod,

    /// This is a sub-category of payment method.
    #[schema(value_type = Option<PaymentMethodType>,example = "credit_card")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,

    /// The name of the bank/ provider issuing the payment method to the end user
    #[schema(example = "Citibank")]
    pub payment_method_issuer: Option<String>,

    /// A standard code representing the issuer of payment method
    #[schema(value_type = Option<PaymentMethodIssuerCode>,example = "jp_applepay")]
    pub payment_method_issuer_code: Option<api_enums::PaymentMethodIssuerCode>,

    /// Indicates whether the payment method supports recurring payments. Optional.
    #[schema(example = true)]
    pub recurring_enabled: Option<bool>,

    /// Indicates whether the payment method is eligible for installment payments (e.g., EMI, BNPL). Optional.
    #[schema(example = true)]
    pub installment_payment_enabled: Option<bool>,

    /// Type of payment experience enabled with the connector
    #[schema(value_type = Option<Vec<PaymentExperience>>,example = json!(["redirect_to_url"]))]
    pub payment_experience: Option<Vec<api_enums::PaymentExperience>>,

    /// Card details from card locker
    #[schema(example = json!({"last4": "1142","exp_month": "03","exp_year": "2030"}))]
    pub card: Option<CardDetailFromLocker>,

    /// 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.
    #[schema(value_type = Option<Object>,example = json!({ "city": "NY", "unit": "245" }))]
    pub metadata: Option<pii::SecretSerdeValue>,

    /// A timestamp (ISO 8601 code) that determines when the payment method was created
    #[schema(value_type = Option<PrimitiveDateTime>,example = "2023-01-18T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,

    /// Payment method details from locker
    #[cfg(feature = "payouts")]
    #[schema(value_type = Option<Bank>)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bank_transfer: Option<payouts::Bank>,

    /// Masked bank details from PM auth services
    #[schema(example = json!({"mask": "0000"}))]
    pub bank: Option<MaskedBankDetails>,

    /// Surcharge details for this saved card
    pub surcharge_details: Option<SurchargeDetailsResponse>,

    /// Whether this payment method requires CVV to be collected
    #[schema(example = true)]
    pub requires_cvv: bool,

    ///  A timestamp (ISO 8601 code) that determines when the payment method was last used
    #[schema(value_type = Option<PrimitiveDateTime>,example = "2024-02-24T11:04:09.922Z")]
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub last_used_at: Option<time::PrimitiveDateTime>,
    /// Indicates if the payment method has been set to default or not
    #[schema(example = true)]
    pub default_payment_method_set: bool,

    /// The billing details of the payment method
    #[schema(value_type = Option<Address>)]
    pub billing: Option<payments::Address>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct PaymentMethodCollectLinkRequest {
    /// The unique identifier for the collect link.
    #[schema(value_type = Option<String>, example = "pm_collect_link_2bdacf398vwzq5n422S1")]
    pub pm_collect_link_id: Option<String>,

    /// The unique identifier of the customer.
    #[schema(value_type = String, example = "cus_92dnwed8s32bV9D8Snbiasd8v")]
    pub customer_id: id_type::CustomerId,

    #[serde(flatten)]
    #[schema(value_type = Option<GenericLinkUiConfig>)]
    pub ui_config: Option<link_utils::GenericLinkUiConfig>,

    /// Will be used to expire client secret after certain amount of time to be supplied in seconds
    /// (900) for 15 mins
    #[schema(value_type = Option<u32>, example = 900)]
    pub session_expiry: Option<u32>,

    /// Redirect to this URL post completion
    #[schema(value_type = Option<String>, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1/status")]
    pub return_url: Option<String>,

    /// List of payment methods shown on collect UI
    #[schema(value_type = Option<Vec<EnabledPaymentMethod>>, example = r#"[{"payment_method": "bank_transfer", "payment_method_types": ["ach", "bacs"]}]"#)]
    pub enabled_payment_methods: Option<Vec<link_utils::EnabledPaymentMethod>>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct PaymentMethodCollectLinkResponse {
    /// The unique identifier for the collect link.
    #[schema(value_type = String, example = "pm_collect_link_2bdacf398vwzq5n422S1")]
    pub pm_collect_link_id: String,

    /// The unique identifier of the customer.
    #[schema(value_type = String, example = "cus_92dnwed8s32bV9D8Snbiasd8v")]
    pub customer_id: id_type::CustomerId,

    /// Time when this link will be expired in ISO8601 format
    #[schema(value_type = PrimitiveDateTime, example = "2025-01-18T11:04:09.922Z")]
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub expiry: time::PrimitiveDateTime,

    /// URL to the form's link generated for collecting payment method details.
    #[schema(value_type = String, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1")]
    pub link: masking::Secret<url::Url>,

    /// Redirect to this URL post completion
    #[schema(value_type = Option<String>, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1/status")]
    pub return_url: Option<String>,

    /// Collect link config used
    #[serde(flatten)]
    #[schema(value_type = GenericLinkUiConfig)]
    pub ui_config: link_utils::GenericLinkUiConfig,

    /// List of payment methods shown on collect UI
    #[schema(value_type = Option<Vec<EnabledPaymentMethod>>, example = r#"[{"payment_method": "bank_transfer", "payment_method_types": ["ach", "bacs"]}]"#)]
    pub enabled_payment_methods: Option<Vec<link_utils::EnabledPaymentMethod>>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct PaymentMethodCollectLinkRenderRequest {
    /// Unique identifier for a merchant.
    #[schema(example = "merchant_1671528864", value_type = String)]
    pub merchant_id: id_type::MerchantId,

    /// The unique identifier for the collect link.
    #[schema(value_type = String, example = "pm_collect_link_2bdacf398vwzq5n422S1")]
    pub pm_collect_link_id: String,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct PaymentMethodCollectLinkDetails {
    pub publishable_key: masking::Secret<String>,
    pub client_secret: masking::Secret<String>,
    pub pm_collect_link_id: String,
    pub customer_id: id_type::CustomerId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub session_expiry: time::PrimitiveDateTime,
    pub return_url: Option<String>,
    #[serde(flatten)]
    pub ui_config: link_utils::GenericLinkUiConfigFormData,
    pub enabled_payment_methods: Option<Vec<link_utils::EnabledPaymentMethod>>,
}

#[derive(Clone, Debug, serde::Serialize)]
pub struct PaymentMethodCollectLinkStatusDetails {
    pub pm_collect_link_id: String,
    pub customer_id: id_type::CustomerId,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub session_expiry: time::PrimitiveDateTime,
    pub return_url: Option<url::Url>,
    pub status: link_utils::PaymentMethodCollectStatus,
    #[serde(flatten)]
    pub ui_config: link_utils::GenericLinkUiConfigFormData,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct MaskedBankDetails {
    pub mask: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentMethodId {
    pub payment_method_id: String,
}

#[cfg(feature = "v1")]
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)]
pub struct DefaultPaymentMethod {
    #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")]
    pub customer_id: id_type::CustomerId,
    pub payment_method_id: String,
}

//------------------------------------------------TokenizeService------------------------------------------------
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizePayloadEncrypted {
    pub payload: String,
    pub key_id: String,
    pub version: Option<String>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizePayloadRequest {
    pub value1: String,
    pub value2: String,
    pub lookup_key: String,
    pub service_name: String,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct GetTokenizePayloadRequest {
    pub lookup_key: String,
    pub service_name: String,
    pub get_value2: bool,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct DeleteTokenizeByTokenRequest {
    pub lookup_key: String,
    pub service_name: String,
}

#[derive(Debug, serde::Serialize)] // Blocked: Yet to be implemented by `basilisk`
pub struct DeleteTokenizeByDateRequest {
    pub buffer_minutes: i32,
    pub service_name: String,
    pub max_rows: i32,
}

#[derive(Debug, serde::Deserialize)]
pub struct GetTokenizePayloadResponse {
    pub lookup_key: String,
    pub get_value2: Option<bool>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizedCardValue1 {
    pub card_number: String,
    pub exp_year: String,
    pub exp_month: String,
    pub name_on_card: Option<String>,
    pub nickname: Option<String>,
    pub card_last_four: Option<String>,
    pub card_token: Option<String>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListCountriesCurrenciesRequest {
    pub connector: api_enums::Connector,
    pub payment_method_type: api_enums::PaymentMethodType,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListCountriesCurrenciesResponse {
    pub currencies: HashSet<api_enums::Currency>,
    pub countries: HashSet<CountryCodeWithName>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize, Eq, Hash, PartialEq)]
pub struct CountryCodeWithName {
    pub code: api_enums::CountryAlpha2,
    pub name: api_enums::Country,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizedCardValue2 {
    pub card_security_code: Option<String>,
    pub card_fingerprint: Option<String>,
    pub external_id: Option<String>,
    pub customer_id: Option<id_type::CustomerId>,
    pub payment_method_id: Option<String>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedWalletValue1 {
    pub data: payments::WalletData,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedWalletValue2 {
    pub customer_id: Option<id_type::CustomerId>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedBankTransferValue1 {
    pub data: payments::BankTransferData,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedBankTransferValue2 {
    pub customer_id: Option<id_type::CustomerId>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedBankRedirectValue1 {
    pub data: payments::BankRedirectData,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizedBankRedirectValue2 {
    pub customer_id: Option<id_type::CustomerId>,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct PaymentMethodRecord {
    pub customer_id: id_type::CustomerId,
    pub name: Option<masking::Secret<String>>,
    pub email: Option<pii::Email>,
    pub phone: Option<masking::Secret<String>>,
    pub phone_country_code: Option<String>,
    pub merchant_id: Option<id_type::MerchantId>,
    pub payment_method: Option<api_enums::PaymentMethod>,
    pub payment_method_type: Option<api_enums::PaymentMethodType>,
    pub nick_name: Option<masking::Secret<String>>,
    pub payment_instrument_id: Option<masking::Secret<String>>,
    pub connector_customer_id: Option<String>,
    pub card_number_masked: masking::Secret<String>,
    pub card_expiry_month: masking::Secret<String>,
    pub card_expiry_year: masking::Secret<String>,
    pub card_scheme: Option<String>,
    pub original_transaction_id: Option<String>,
    pub billing_address_zip: Option<masking::Secret<String>>,
    pub billing_address_state: Option<masking::Secret<String>>,
    pub billing_address_first_name: Option<masking::Secret<String>>,
    pub billing_address_last_name: Option<masking::Secret<String>>,
    pub billing_address_city: Option<String>,
    pub billing_address_country: Option<api_enums::CountryAlpha2>,
    pub billing_address_line1: Option<masking::Secret<String>>,
    pub billing_address_line2: Option<masking::Secret<String>>,
    pub billing_address_line3: Option<masking::Secret<String>>,
    pub raw_card_number: Option<masking::Secret<String>>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub merchant_connector_ids: Option<String>,
    pub original_transaction_amount: Option<i64>,
    pub original_transaction_currency: Option<common_enums::Currency>,
    pub line_number: Option<i64>,
    pub network_token_number: Option<CardNumber>,
    pub network_token_expiry_month: Option<masking::Secret<String>>,
    pub network_token_expiry_year: Option<masking::Secret<String>>,
    pub network_token_requestor_ref_id: Option<String>,
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct UpdatePaymentMethodRecord {
    pub payment_method_id: String,
    pub status: Option<common_enums::PaymentMethodStatus>,
    pub network_transaction_id: Option<String>,
    pub line_number: Option<i64>,
    pub payment_instrument_id: Option<masking::Secret<String>>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
}

#[derive(Debug, serde::Serialize)]
pub struct PaymentMethodUpdateResponse {
    pub payment_method_id: String,
    pub status: Option<common_enums::PaymentMethodStatus>,
    pub network_transaction_id: Option<String>,
    pub connector_mandate_details: Option<pii::SecretSerdeValue>,
    pub update_status: UpdateStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub update_error: Option<String>,
    pub line_number: Option<i64>,
}

#[derive(Debug, Default, serde::Serialize)]
pub struct PaymentMethodMigrationResponse {
    pub line_number: Option<i64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method: Option<api_enums::PaymentMethod>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub payment_method_type: Option<api_enums::PaymentMethodType>,
    pub customer_id: Option<id_type::CustomerId>,
    pub migration_status: MigrationStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub migration_error: Option<String>,
    pub card_number_masked: Option<masking::Secret<String>>,
    pub card_migrated: Option<bool>,
    pub network_token_migrated: Option<bool>,
    pub connector_mandate_details_migrated: Option<bool>,
    pub network_transaction_id_migrated: Option<bool>,
}

#[derive(Debug, Default, serde::Serialize)]
pub enum MigrationStatus {
    Success,
    #[default]
    Failed,
}

#[derive(Debug, Default, serde::Serialize)]
pub enum UpdateStatus {
    Success,
    #[default]
    Failed,
}


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

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

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

impl webhooks::IncomingWebhook for for Thunes


pub async fn list_all_themes_in_lineage(
    state: SessionState,
    user: UserFromToken,
    entity_type: EntityType,
) -> UserResponse<Vec<theme_api::GetThemeResponse>> {
    let lineage =
        theme_utils::get_theme_lineage_from_user_token(&user, &state, &entity_type).await?;

    let db_themes = state
        .store
        .list_themes_at_and_under_lineage(lineage)
        .await
        .change_context(UserErrors::InternalServerError)?;

    let mut themes = Vec::new();
    for theme in db_themes {
        match theme_utils::retrieve_file_from_theme_bucket(
            &state,
            &theme_utils::get_theme_file_key(&theme.theme_id),
        )
        .await
        {
            Ok(file) => {
                match file
                    .to_bytes()
                    .parse_struct("ThemeData")
                    .change_context(UserErrors::InternalServerError)
                {
                    Ok(parsed_data) => {
                        themes.push(theme_api::GetThemeResponse {
                            email_config: theme.email_config(),
                            theme_id: theme.theme_id,
                            theme_name: theme.theme_name,
                            entity_type: theme.entity_type,
                            tenant_id: theme.tenant_id,
                            org_id: theme.org_id,
                            merchant_id: theme.merchant_id,
                            profile_id: theme.profile_id,
                            theme_data: parsed_data,
                        });
                    }
                    Err(_) => {
                        return Err(UserErrors::ErrorRetrievingFile.into());
                    }
                }
            }
            Err(_) => {
                return Err(UserErrors::ErrorRetrievingFile.into());
            }
        }
    }

    Ok(ApplicationResponse::Json(themes))
}


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 struct VolumeSplit<T> {
    pub split: u8,
    pub output: T,
}


/// Get the entity_type from the lineage
    pub fn entity_type(&self) -> EntityType {
        match self {
            Self::Tenant { .. } => EntityType::Tenant,
            Self::Organization { .. } => EntityType::Organization,
            Self::Merchant { .. } => EntityType::Merchant,
            Self::Profile { .. } => EntityType::Profile,
        }
    }


File: crates/analytics/src/payments/metrics/payment_processed_amount.rs

use std::collections::HashSet;

use api_models::analytics::{
    payments::{PaymentDimensions, PaymentFilters, PaymentMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use diesel_models::enums as storage_enums;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::PaymentMetricRow;
use crate::{
    enums::AuthInfo,
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};

#[derive(Default)]
pub(super) struct PaymentProcessedAmount;

#[async_trait::async_trait]
impl<T> super::PaymentMetric<T> for PaymentProcessedAmount
where
    T: AnalyticsDataSource + super::PaymentMetricAnalytics,
    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: &[PaymentDimensions],
        auth: &AuthInfo,
        filters: &PaymentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>> {
        let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::Payment);

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

        query_builder
            .add_select_column(Aggregate::Sum {
                field: "amount",
                alias: Some("total"),
            })
            .switch()?;
        query_builder.add_select_column("currency").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)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }

        query_builder
            .add_group_by_clause("currency")
            .attach_printable("Error grouping by currency")
            .switch()?;

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

        query_builder
            .add_filter_clause(
                PaymentDimensions::PaymentStatus,
                storage_enums::AttemptStatus::Charged,
            )
            .switch()?;

        query_builder
            .execute_query::<PaymentMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    PaymentMetricsBucketIdentifier::new(
                        i.currency.as_ref().map(|i| i.0),
                        None,
                        i.connector.clone(),
                        i.authentication_type.as_ref().map(|i| i.0),
                        i.payment_method.clone(),
                        i.payment_method_type.clone(),
                        i.client_source.clone(),
                        i.client_version.clone(),
                        i.profile_id.clone(),
                        i.card_network.clone(),
                        i.merchant_id.clone(),
                        i.card_last_4.clone(),
                        i.card_issuer.clone(),
                        i.error_reason.clone(),
                        i.routing_approach.as_ref().map(|i| i.0.clone()),
                        i.signature_network.clone(),
                        i.is_issuer_regulated,
                        i.is_debit_routed,
                        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<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


pub async fn delete(
        conn: &PgPooledConn,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> StorageResult<bool> {
        generics::generic_delete::<<Self as HasTable>::Table, _>(
            conn,
            dsl::connector
                .eq(connector)
                .and(dsl::flow.eq(flow))
                .and(dsl::sub_flow.eq(sub_flow))
                .and(dsl::code.eq(code))
                .and(dsl::message.eq(message)),
        )
        .await
    }


pub struct MerchantAdvice {
    code: Option<String>,
    code_raw: Option<String>,
}


pub struct Shift4NonThreeDsResponse {
    pub id: String,
    pub currency: String,
    pub amount: u32,
    pub status: Shift4PaymentStatus,
    pub captured: bool,
    pub refunded: bool,
    pub flow: Option<FlowResponse>,
}


impl Responder {
    let gsm_retrieve_req = json_payload.into_inner();
    let flow = Flow::GsmRuleRetrieve;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        gsm_retrieve_req,
        |state, _, gsm_retrieve_req, _| gsm::retrieve_gsm_rule(state, gsm_retrieve_req),
        &ADMIN_API_AUTH,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn decide_connector(
    state: SessionState,
    merchant_context: &domain::MerchantContext,
    business_profile: &domain::Profile,
    routing_data: &mut storage::RoutingData,
    payment_dsl_input: core_routing::PaymentsDslInput<'_>,
    mandate_type: Option<api::MandateTransactionType>,
) -> RouterResult<ConnectorCallType> {
    // If the connector was already decided previously, use the same connector
    // This is in case of flows like payments_sync, payments_cancel where the successive operations
    // with the connector have to be made using the same connector account.

    let predetermined_info_cloned = routing_data
        .routed_through
        .as_ref()
        .zip(routing_data.merchant_connector_id.as_ref())
        .map(|(cn_ref, mci_ref)| (cn_ref.clone(), mci_ref.clone()));

    match (
        predetermined_info_cloned,
        routing_data.pre_routing_connector_choice.as_ref(),
    ) {
        // Condition 1: Connector was already decided previously
        (Some((owned_connector_name, owned_merchant_connector_id)), _) => {
            api::ConnectorData::get_connector_by_name(
                &state.conf.connectors,
                &owned_connector_name,
                api::GetToken::Connector,
                Some(owned_merchant_connector_id.clone()),
            )
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Invalid connector name received in 'routed_through'")
            .map(|connector_data| {
                routing_data.routed_through = Some(owned_connector_name);
                ConnectorCallType::PreDetermined(connector_data.into())
            })
        }
        // Condition 2: Pre-routing connector choice
        (None, Some(routable_connector_choice)) => {
            let routable_connector_list = match routable_connector_choice {
                storage::PreRoutingConnectorChoice::Single(routable_connector) => {
                    vec![routable_connector.clone()]
                }
                storage::PreRoutingConnectorChoice::Multiple(routable_connector_list) => {
                    routable_connector_list.clone()
                }
            };

            routable_connector_list
                .first()
                .ok_or_else(|| {
                    report!(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
                        .attach_printable("No first routable connector in pre_routing_connector_choice")
                })
                .and_then(|first_routable_connector| {
                    routing_data.routed_through = Some(first_routable_connector.connector.to_string());
                    routing_data
                        .merchant_connector_id
                        .clone_from(&first_routable_connector.merchant_connector_id);

                    let pre_routing_connector_data_list_result: RouterResult<Vec<api::ConnectorData>> = routable_connector_list
                        .iter()
                        .map(|connector_choice| {
                            api::ConnectorData::get_connector_by_name(
                                &state.conf.connectors,
                                &connector_choice.connector.to_string(),
                                api::GetToken::Connector,
                                connector_choice.merchant_connector_id.clone(),
                            )
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Invalid connector name received while processing pre_routing_connector_choice")
                        })
                        .collect::<Result<Vec<_>, _>>(); // Collects into RouterResult<Vec<ConnectorData>>

                    pre_routing_connector_data_list_result
                        .and_then(|list| {
                            list.first()
                                .cloned()
                                .ok_or_else(|| {
                                    report!(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
                                        .attach_printable("Empty pre_routing_connector_data_list after mapping")
                                })
                                .map(|first_data| ConnectorCallType::PreDetermined(first_data.into()))
                        })
                })
        }
        (None, None) => {
            route_connector_v2_for_payments(
                &state,
                merchant_context,
                business_profile,
                payment_dsl_input,
                routing_data,
                mandate_type,
            )
            .await
        }
    }
}


pub async fn payments_core<F, Res, Req, Op, FData, D>(
    state: SessionState,
    req_state: ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    operation: Op,
    req: Req,
    payment_id: id_type::GlobalPaymentId,
    call_connector_action: CallConnectorAction,
    header_payload: HeaderPayload,
) -> RouterResponse<Res>
where
    F: Send + Clone + Sync,
    Req: Send + Sync + Authenticate,
    FData: Send + Sync + Clone,
    Op: Operation<F, Req, Data = D> + ValidateStatusForOperation + Send + Sync + Clone,
    Req: Debug,
    D: OperationSessionGetters<F>
        + OperationSessionSetters<F>
        + transformers::GenerateResponse<Res>
        + Send
        + Sync
        + Clone,
    // To create connector flow specific interface data
    D: ConstructFlowSpecificData<F, FData, router_types::PaymentsResponseData>,
    RouterData<F, FData, router_types::PaymentsResponseData>: Feature<F, FData>,

    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, FData, router_types::PaymentsResponseData>,

    // To perform router related operation for PaymentResponse
    PaymentResponse: Operation<F, FData, Data = D>,

    // To create updatable objects in post update tracker
    RouterData<F, FData, router_types::PaymentsResponseData>:
        hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<F, FData, D>,
{
    // Validate the request fields
    operation
        .to_validate_request()?
        .validate_request(&req, &merchant_context)?;

    // Get the tracker related information. This includes payment intent and payment attempt
    let get_tracker_response = operation
        .to_get_tracker()?
        .get_trackers(
            &state,
            &payment_id,
            &req,
            &merchant_context,
            &profile,
            &header_payload,
        )
        .await?;

    let (payment_data, connector_http_status_code, external_latency, connector_response_data) =
        if state.conf.merchant_id_auth.merchant_id_auth_enabled {
            let (
                payment_data,
                _req,
                connector_http_status_code,
                external_latency,
                connector_response_data,
            ) = internal_payments_operation_core::<_, _, _, _, _>(
                &state,
                req_state,
                merchant_context.clone(),
                &profile,
                operation.clone(),
                req,
                get_tracker_response,
                call_connector_action,
                header_payload.clone(),
            )
            .await?;
            (
                payment_data,
                connector_http_status_code,
                external_latency,
                connector_response_data,
            )
        } else {
            let (
                payment_data,
                _req,
                _customer,
                connector_http_status_code,
                external_latency,
                connector_response_data,
            ) = payments_operation_core::<_, _, _, _, _>(
                &state,
                req_state,
                merchant_context.clone(),
                &profile,
                operation.clone(),
                req,
                get_tracker_response,
                call_connector_action,
                header_payload.clone(),
            )
            .await?;
            (
                payment_data,
                connector_http_status_code,
                external_latency,
                connector_response_data,
            )
        };

    payment_data.generate_response(
        &state,
        connector_http_status_code,
        external_latency,
        header_payload.x_hs_latency,
        &merchant_context,
        &profile,
        Some(connector_response_data),
    )
}


impl ResponseId {
    pub fn get_connector_transaction_id(
        &self,
    ) -> errors::CustomResult<String, errors::ValidationError> {
        match self {
            Self::ConnectorTransactionId(txn_id) => Ok(txn_id.to_string()),
            _ => Err(errors::ValidationError::IncorrectValueProvided {
                field_name: "connector_transaction_id",
            })
            .attach_printable("Expected connector transaction ID not found"),
        }
    }
}


impl Responder {
    let wrapper = routing_types::ProfileDefaultRoutingConfig {
        profile_id: path.into_inner(),
        connectors: json_payload.into_inner(),
    };
    Box::pin(oss_api::server_wrap(
        Flow::RoutingUpdateDefaultConfig,
        state,
        &req,
        wrapper,
        |state, auth: auth::AuthenticationData, wrapper, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            routing::update_default_fallback_routing(
                state,
                merchant_context,
                wrapper.profile_id,
                wrapper.connectors,
            )
        },
        #[cfg(not(feature = "release"))]
        auth::auth_type(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::JWTAuth {
                permission: Permission::MerchantRoutingWrite,
            },
            req.headers(),
        ),
        #[cfg(feature = "release")]
        &auth::JWTAuth {
            permission: Permission::MerchantRoutingWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct AuthenticationDataWithOrg {
    pub organization_id: id_type::OrganizationId,
}


impl Display for GenericLinksData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match *self {
                Self::ExpiredLink(_) => "ExpiredLink",
                Self::PaymentMethodCollect(_) => "PaymentMethodCollect",
                Self::PayoutLink(_) => "PayoutLink",
                Self::PayoutLinkStatus(_) => "PayoutLinkStatus",
                Self::PaymentMethodCollectStatus(_) => "PaymentMethodCollectStatus",
                Self::SecurePaymentLink(_) => "SecurePaymentLink",
            }
        )
    }
}


impl api::revenue_recovery_v2::BillingConnectorInvoiceSyncIntegrationV2 for Recurly {}

impl api::revenue_recovery_v2::BillingConnectorInvoiceSyncIntegrationV2 for for Recurly