File: crates/hyperswitch_domain_models/src/mandates.rs
 Public functions: 4
 Public structs: 11

use std::collections::HashMap;

use api_models::payments::{
    MandateAmountData as ApiMandateAmountData, MandateData as ApiMandateData, MandateType,
};
use common_enums::Currency;
use common_types::payments as common_payments_types;
use common_utils::{
    date_time,
    errors::{CustomResult, ParsingError},
    pii,
    types::MinorUnit,
};
use error_stack::ResultExt;
use time::PrimitiveDateTime;

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct MandateDetails {
    pub update_mandate_id: Option<String>,
}

impl From<MandateDetails> for diesel_models::enums::MandateDetails {
    fn from(value: MandateDetails) -> Self {
        Self {
            update_mandate_id: value.update_mandate_id,
        }
    }
}

impl From<diesel_models::enums::MandateDetails> for MandateDetails {
    fn from(value: diesel_models::enums::MandateDetails) -> Self {
        Self {
            update_mandate_id: value.update_mandate_id,
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum MandateDataType {
    SingleUse(MandateAmountData),
    MultiUse(Option<MandateAmountData>),
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct MandateAmountData {
    pub amount: MinorUnit,
    pub currency: Currency,
    pub start_date: Option<PrimitiveDateTime>,
    pub end_date: Option<PrimitiveDateTime>,
    pub metadata: Option<pii::SecretSerdeValue>,
}

// The fields on this struct are optional, as we want to allow the merchant to provide partial
// information about creating mandates
#[derive(Default, Eq, PartialEq, Debug, Clone)]
pub struct MandateData {
    /// A way to update the mandate's payment method details
    pub update_mandate_id: Option<String>,
    /// A consent from the customer to store the payment method
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    /// A way to select the type of mandate used
    pub mandate_type: Option<MandateDataType>,
}

impl From<MandateType> for MandateDataType {
    fn from(mandate_type: MandateType) -> Self {
        match mandate_type {
            MandateType::SingleUse(mandate_amount_data) => {
                Self::SingleUse(mandate_amount_data.into())
            }
            MandateType::MultiUse(mandate_amount_data) => {
                Self::MultiUse(mandate_amount_data.map(|d| d.into()))
            }
        }
    }
}

impl From<MandateDataType> for diesel_models::enums::MandateDataType {
    fn from(value: MandateDataType) -> Self {
        match value {
            MandateDataType::SingleUse(data) => Self::SingleUse(data.into()),
            MandateDataType::MultiUse(None) => Self::MultiUse(None),
            MandateDataType::MultiUse(Some(data)) => Self::MultiUse(Some(data.into())),
        }
    }
}

impl From<diesel_models::enums::MandateDataType> for MandateDataType {
    fn from(value: diesel_models::enums::MandateDataType) -> Self {
        use diesel_models::enums::MandateDataType as DieselMandateDataType;

        match value {
            DieselMandateDataType::SingleUse(data) => Self::SingleUse(data.into()),
            DieselMandateDataType::MultiUse(None) => Self::MultiUse(None),
            DieselMandateDataType::MultiUse(Some(data)) => Self::MultiUse(Some(data.into())),
        }
    }
}

impl From<ApiMandateAmountData> for MandateAmountData {
    fn from(value: ApiMandateAmountData) -> Self {
        Self {
            amount: value.amount,
            currency: value.currency,
            start_date: value.start_date,
            end_date: value.end_date,
            metadata: value.metadata,
        }
    }
}

impl From<MandateAmountData> for diesel_models::enums::MandateAmountData {
    fn from(value: MandateAmountData) -> Self {
        Self {
            amount: value.amount,
            currency: value.currency,
            start_date: value.start_date,
            end_date: value.end_date,
            metadata: value.metadata,
        }
    }
}

impl From<diesel_models::enums::MandateAmountData> for MandateAmountData {
    fn from(value: diesel_models::enums::MandateAmountData) -> Self {
        Self {
            amount: value.amount,
            currency: value.currency,
            start_date: value.start_date,
            end_date: value.end_date,
            metadata: value.metadata,
        }
    }
}

impl From<ApiMandateData> for MandateData {
    fn from(value: ApiMandateData) -> Self {
        Self {
            customer_acceptance: value.customer_acceptance,
            mandate_type: value.mandate_type.map(|d| d.into()),
            update_mandate_id: value.update_mandate_id,
        }
    }
}

impl MandateAmountData {
    pub fn get_end_date(
        &self,
        format: date_time::DateFormat,
    ) -> error_stack::Result<Option<String>, ParsingError> {
        self.end_date
            .map(|date| {
                date_time::format_date(date, format)
                    .change_context(ParsingError::DateTimeParsingError)
            })
            .transpose()
    }
    pub fn get_metadata(&self) -> Option<pii::SecretSerdeValue> {
        self.metadata.clone()
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsMandateReferenceRecord {
    pub connector_mandate_id: String,
    pub payment_method_type: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<i64>,
    pub original_payment_authorized_currency: Option<Currency>,
    pub mandate_metadata: Option<pii::SecretSerdeValue>,
    pub connector_mandate_status: Option<common_enums::ConnectorMandateStatus>,
    pub connector_mandate_request_reference_id: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConnectorTokenReferenceRecord {
    pub connector_token: String,
    pub payment_method_subtype: Option<common_enums::PaymentMethodType>,
    pub original_payment_authorized_amount: Option<MinorUnit>,
    pub original_payment_authorized_currency: Option<Currency>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_token_status: common_enums::ConnectorTokenStatus,
    pub connector_token_request_reference_id: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PayoutsMandateReferenceRecord {
    pub transfer_method_id: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PayoutsMandateReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>,
);

impl std::ops::Deref for PayoutsMandateReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, PayoutsMandateReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl std::ops::DerefMut for PayoutsMandateReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsTokenReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, ConnectorTokenReferenceRecord>,
);

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentsMandateReference(
    pub HashMap<common_utils::id_type::MerchantConnectorAccountId, PaymentsMandateReferenceRecord>,
);

#[cfg(feature = "v1")]
impl std::ops::Deref for PaymentsMandateReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, PaymentsMandateReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "v1")]
impl std::ops::DerefMut for PaymentsMandateReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v2")]
impl std::ops::Deref for PaymentsTokenReference {
    type Target =
        HashMap<common_utils::id_type::MerchantConnectorAccountId, ConnectorTokenReferenceRecord>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "v2")]
impl std::ops::DerefMut for PaymentsTokenReference {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CommonMandateReference {
    pub payments: Option<PaymentsMandateReference>,
    pub payouts: Option<PayoutsMandateReference>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CommonMandateReference {
    pub payments: Option<PaymentsTokenReference>,
    pub payouts: Option<PayoutsMandateReference>,
}

impl CommonMandateReference {
    pub fn get_mandate_details_value(&self) -> CustomResult<serde_json::Value, ParsingError> {
        let mut payments = self
            .payments
            .as_ref()
            .map_or_else(|| Ok(serde_json::json!({})), serde_json::to_value)
            .change_context(ParsingError::StructParseFailure("payment mandate details"))?;

        self.payouts
            .as_ref()
            .map(|payouts_mandate| {
                serde_json::to_value(payouts_mandate).map(|payouts_mandate_value| {
                    payments.as_object_mut().map(|payments_object| {
                        payments_object.insert("payouts".to_string(), payouts_mandate_value);
                    })
                })
            })
            .transpose()
            .change_context(ParsingError::StructParseFailure("payout mandate details"))?;

        Ok(payments)
    }

    #[cfg(feature = "v2")]
    /// Insert a new payment token reference for the given connector_id
    pub fn insert_payment_token_reference_record(
        &mut self,
        connector_id: &common_utils::id_type::MerchantConnectorAccountId,
        record: ConnectorTokenReferenceRecord,
    ) {
        match self.payments {
            Some(ref mut payments_reference) => {
                payments_reference.insert(connector_id.clone(), record);
            }
            None => {
                let mut payments_reference = HashMap::new();
                payments_reference.insert(connector_id.clone(), record);
                self.payments = Some(PaymentsTokenReference(payments_reference));
            }
        }
    }
}

impl From<diesel_models::CommonMandateReference> for CommonMandateReference {
    fn from(value: diesel_models::CommonMandateReference) -> Self {
        Self {
            payments: value.payments.map(|payments| payments.into()),
            payouts: value.payouts.map(|payouts| payouts.into()),
        }
    }
}

impl From<CommonMandateReference> for diesel_models::CommonMandateReference {
    fn from(value: CommonMandateReference) -> Self {
        Self {
            payments: value.payments.map(|payments| payments.into()),
            payouts: value.payouts.map(|payouts| payouts.into()),
        }
    }
}

impl From<diesel_models::PayoutsMandateReference> for PayoutsMandateReference {
    fn from(value: diesel_models::PayoutsMandateReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

impl From<PayoutsMandateReference> for diesel_models::PayoutsMandateReference {
    fn from(value: PayoutsMandateReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

#[cfg(feature = "v1")]
impl From<diesel_models::PaymentsMandateReference> for PaymentsMandateReference {
    fn from(value: diesel_models::PaymentsMandateReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

#[cfg(feature = "v1")]
impl From<PaymentsMandateReference> for diesel_models::PaymentsMandateReference {
    fn from(value: PaymentsMandateReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

#[cfg(feature = "v2")]
impl From<diesel_models::PaymentsTokenReference> for PaymentsTokenReference {
    fn from(value: diesel_models::PaymentsTokenReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

#[cfg(feature = "v2")]
impl From<PaymentsTokenReference> for diesel_models::PaymentsTokenReference {
    fn from(value: PaymentsTokenReference) -> Self {
        Self(
            value
                .0
                .into_iter()
                .map(|(key, record)| (key, record.into()))
                .collect(),
        )
    }
}

impl From<diesel_models::PayoutsMandateReferenceRecord> for PayoutsMandateReferenceRecord {
    fn from(value: diesel_models::PayoutsMandateReferenceRecord) -> Self {
        Self {
            transfer_method_id: value.transfer_method_id,
        }
    }
}

impl From<PayoutsMandateReferenceRecord> for diesel_models::PayoutsMandateReferenceRecord {
    fn from(value: PayoutsMandateReferenceRecord) -> Self {
        Self {
            transfer_method_id: value.transfer_method_id,
        }
    }
}

#[cfg(feature = "v2")]
impl From<diesel_models::ConnectorTokenReferenceRecord> for ConnectorTokenReferenceRecord {
    fn from(value: diesel_models::ConnectorTokenReferenceRecord) -> Self {
        let diesel_models::ConnectorTokenReferenceRecord {
            connector_token,
            payment_method_subtype,
            original_payment_authorized_amount,
            original_payment_authorized_currency,
            metadata,
            connector_token_status,
            connector_token_request_reference_id,
        } = value;
        Self {
            connector_token,
            payment_method_subtype,
            original_payment_authorized_amount,
            original_payment_authorized_currency,
            metadata,
            connector_token_status,
            connector_token_request_reference_id,
        }
    }
}

#[cfg(feature = "v1")]
impl From<diesel_models::PaymentsMandateReferenceRecord> for PaymentsMandateReferenceRecord {
    fn from(value: diesel_models::PaymentsMandateReferenceRecord) -> Self {
        Self {
            connector_mandate_id: value.connector_mandate_id,
            payment_method_type: value.payment_method_type,
            original_payment_authorized_amount: value.original_payment_authorized_amount,
            original_payment_authorized_currency: value.original_payment_authorized_currency,
            mandate_metadata: value.mandate_metadata,
            connector_mandate_status: value.connector_mandate_status,
            connector_mandate_request_reference_id: value.connector_mandate_request_reference_id,
        }
    }
}

#[cfg(feature = "v2")]
impl From<ConnectorTokenReferenceRecord> for diesel_models::ConnectorTokenReferenceRecord {
    fn from(value: ConnectorTokenReferenceRecord) -> Self {
        let ConnectorTokenReferenceRecord {
            connector_token,
            payment_method_subtype,
            original_payment_authorized_amount,
            original_payment_authorized_currency,
            metadata,
            connector_token_status,
            connector_token_request_reference_id,
        } = value;
        Self {
            connector_token,
            payment_method_subtype,
            original_payment_authorized_amount,
            original_payment_authorized_currency,
            metadata,
            connector_token_status,
            connector_token_request_reference_id,
        }
    }
}

#[cfg(feature = "v1")]
impl From<PaymentsMandateReferenceRecord> for diesel_models::PaymentsMandateReferenceRecord {
    fn from(value: PaymentsMandateReferenceRecord) -> Self {
        Self {
            connector_mandate_id: value.connector_mandate_id,
            payment_method_type: value.payment_method_type,
            original_payment_authorized_amount: value.original_payment_authorized_amount,
            original_payment_authorized_currency: value.original_payment_authorized_currency,
            mandate_metadata: value.mandate_metadata,
            connector_mandate_status: value.connector_mandate_status,
            connector_mandate_request_reference_id: value.connector_mandate_request_reference_id,
        }
    }
}


OpenAPI Block Path: components.schemas.RoutingVolumeSplitResponse
{
  "type": "object",
  "required": [
    "split"
  ],
  "properties": {
    "split": {
      "type": "integer",
      "format": "int32",
      "minimum": 0
    }
  }
}


Web Documentation: Alternate Payment Method Widgets | Hyperswitch
# Type: Web Doc

We support modular, embeddable alternate payment methods (APMs) known as
Hyperwidgets
, which a merchant can use to augment their existing checkout in a low code manner.
Let us understand with an example-
In the below checkout screen the merchant only has Cards and Paypal offered by their current PSP.
But, if the merchant decides to enable more alternate payment methods (APMs), the problems can be multifold:
Availability
- Current PSP may not support the desired alternate payment method requiring the merchant to do a direct integration or use a different PSP.
Complex to integrate
- In case a PSP supports a desired payment method, integrating it into the existing checkout is tedious and often requires high engineering effort. Hyperwidets offers a low code solution for such a scenario where a merchant can not only choose to have their preferred payment method through a wide range of connectors, but also integrate it with minimal engineering effort.
Complex to enhance
- Traditional PSPs and Middle layers ((Subscription provider’s SDK or Token provider’s SDK or Orchestration provider’s SDK)) offer separate integration for every new APM, requiring ongoing engineering effort for enhancements.
Hyperwidets offers a way for merchants to extend APMs from just “Apple Pay” to “Apple Pay + Google Pay” to “Apple Pay + Google Pay + Amazon Pay + 10 more” with the same integration and no additional engineering effort.
Hyperwidgets also offers merchants the flexibility to surface the right set of APMs in the widget based on the order context - Value, Region and more.
Integration overhead to enable more APMs on the same PSP
: Some PSPs require the merchant to do some additional integration steps to enable an APM. This can be time consuming and require significant tech bandwidth from the merchant. Examples can be when certain APMs are only available on new API versions of the PSP, certification from Apple/ Google is required to go-live for some wallet flows, adding a frontend library/ javascript to add certain APMs, and so on.
in the image below you can find a checkout page with more alternate payment methods via Hyperwidgets.
Additional example:
How is Hyperwidget engineered:
Hyperwidgets are engineered to simplify and streamline the integration of alternate payment methods (APMs) for merchants, regardless of their existing payment setup. The design focuses on providing a modular, embeddable, and low-code solution, reducing the engineering effort traditionally required to add or enhance payment options.
Single SDK to manage all APMs
Hyperwidgets come packaged in a single SDK that merchants can integrate into their web app and route some of their APM traffic to. The widget is powered through a javascript which resides in the merchant's top level domain. Thus, it acts as an extension of the merchant checkout and powerful enough to offer a range of payment methods from Express Checkout payment options (like Applepay, Paypal, etc.), wallets ( like Wechatpay, Alipay, etc.), BNPLs (like Klarna, Affirm, etc.) to Bank Debits and Transfers (like ACH, BACS, SEPA, etc.)
Seamless integration for all tech stacks
The Hyperwidget provides seamless integration for merchants using different frameworks for their web app. This is done using multiple wrappers like React, HTML, Angular, etc. This ensures a low-code, low-effort integration for the merchants.
Configuring PSPs is independent from integration
The Hyperwidget integration is a one time effort. To manage the different payment methods across business lines, geographies, etc. the merchant bears no additional overhead. Adding/ removing PSPs and routing the transaction through different PSPs is a dashboard configuration.
Modular fit with other integrations
Merchants can work with multiple PSPs or middle layers, such as subscription providers or orchestration SDKs, without any conflicts from Hyperwidgets. The Hyperwidgets integration fits seamlessly in their existing checkout flow and business logic. They just need to invoke and handle callbacks at the right triggers in their checkout flow.
High customization
The Hyperwidgets UI is highly customizable and can completely blend in with the merchant app. Using a customization object, merchants can very easily control the UI parameters like background color, corner radius of the buttons,font size, etc. to match their branding. Some of the examples to highlight the customization options of the checkout are -
Unified Analytics and order management
All the transactions that are processed through Hyperwidgets can be seen on a unified dashboard. The merchant can analyse data at a granular level to understand conversion across different payment methods, filter by customer id, transaction type (3DS, no-3DS), get a summary of failure reasons for failed transactions and much more. The unification is done across the PSPs/ Acquirers for each payment method.
Future ready design
The engineering ensures support for emerging payment methods and new PSPs without requiring additional engineering work, ensuring a scalable one-time only integration for the merchants.
Supported merchant setups :
Setup A
- Merchant is directly integrated with 1 PSP and is looking to enable APMs via same or a different PSP
Setup B
- Merchant is indirectly integrated with 1 PSP via a middle layer (Subscription provider’s SDK or Token provider’s SDK or Orchestration provider’s SDK). The merchant is looking to enable APMs via the same or different PSP
via PSP or middle layer
via Hyperwidgets
Existing APMs
Apple Pay -
2-3 weeks of engineering effort
Klarna -
2-3 weeks of engineering effort
New innovations
Paze -
Not available via most players
Click2Pay -
Not available via most player

One time effort of
2-weeks
to enable all existing APMs and new innovations via the same integration
Hyperwidget Integration Guide
18 days ago
Was this helpful?


pub struct RelayRefund;


/// Obtain the crates in the current cargo workspace as a `HashSet`.
///
/// This macro requires that [`set_cargo_workspace_members_env()`] function be called in the
/// build script of the crate where this macro is being called.
///
/// # Errors
///
/// Causes a compilation error if the `CARGO_WORKSPACE_MEMBERS` environment variable is unset.
#[macro_export]
macro_rules! cargo_workspace_members {
    () => {
        std::env!("CARGO_WORKSPACE_MEMBERS")
            .split(',')
            .collect::<std::collections::HashSet<&'static str>>()
    };
}


pub struct TransactionFailedRequest {
    checkout: FailedTransactionData,
}


impl api::PaymentCapture for Checkbook {}

impl api::PaymentCapture for for Checkbook


pub struct MifinityData {
    pub date_of_birth: Secret<Date>,
    pub language_preference: Option<String>,
}


File: crates/api_models/src/external_service_auth.rs
 Public functions: 1
 Public structs: 3

use common_utils::{id_type, pii};
use masking::Secret;

#[derive(Debug, serde::Serialize)]
pub struct ExternalTokenResponse {
    pub token: Secret<String>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct ExternalVerifyTokenRequest {
    pub token: Secret<String>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct ExternalSignoutTokenRequest {
    pub token: Secret<String>,
}

#[derive(serde::Serialize, Debug)]
#[serde(untagged)]
pub enum ExternalVerifyTokenResponse {
    Hypersense {
        user_id: String,
        merchant_id: id_type::MerchantId,
        name: Secret<String>,
        email: pii::Email,
    },
}

impl ExternalVerifyTokenResponse {
    pub fn get_user_id(&self) -> &str {
        match self {
            Self::Hypersense { user_id, .. } => user_id,
        }
    }
}


pub fn get_invoice_webhook_data_from_body(
        body: &[u8],
    ) -> CustomResult<Self, errors::ConnectorError> {
        let webhook_body = body
            .parse_struct::<Self>("ChargebeeInvoiceBody")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        Ok(webhook_body)
    }


impl Default for Mandates {
    fn default() -> Self {
        Self {
            supported_payment_methods: SupportedPaymentMethodsForMandate(HashMap::from([
                (
                    enums::PaymentMethod::PayLater,
                    SupportedPaymentMethodTypesForMandate(HashMap::from([(
                        enums::PaymentMethodType::Klarna,
                        SupportedConnectorsForMandate {
                            connector_list: HashSet::from([Connector::Adyen]),
                        },
                    )])),
                ),
                (
                    enums::PaymentMethod::Wallet,
                    SupportedPaymentMethodTypesForMandate(HashMap::from([
                        (
                            enums::PaymentMethodType::GooglePay,
                            SupportedConnectorsForMandate {
                                connector_list: HashSet::from([
                                    Connector::Stripe,
                                    Connector::Adyen,
                                    Connector::Globalpay,
                                    Connector::Multisafepay,
                                    Connector::Bankofamerica,
                                    Connector::Novalnet,
                                    Connector::Noon,
                                    Connector::Cybersource,
                                    Connector::Wellsfargo,
                                ]),
                            },
                        ),
                        (
                            enums::PaymentMethodType::ApplePay,
                            SupportedConnectorsForMandate {
                                connector_list: HashSet::from([
                                    Connector::Stripe,
                                    Connector::Adyen,
                                    Connector::Bankofamerica,
                                    Connector::Cybersource,
                                    Connector::Novalnet,
                                    Connector::Wellsfargo,
                                ]),
                            },
                        ),
                        (
                            enums::PaymentMethodType::SamsungPay,
                            SupportedConnectorsForMandate {
                                connector_list: HashSet::from([Connector::Cybersource]),
                            },
                        ),
                    ])),
                ),
                (
                    enums::PaymentMethod::Card,
                    SupportedPaymentMethodTypesForMandate(HashMap::from([
                        (
                            enums::PaymentMethodType::Credit,
                            SupportedConnectorsForMandate {
                                connector_list: HashSet::from([
                                    Connector::Aci,
                                    Connector::Adyen,
                                    Connector::Authorizedotnet,
                                    Connector::Globalpay,
                                    Connector::Worldpay,
                                    Connector::Fiuu,
                                    Connector::Multisafepay,
                                    Connector::Nexinets,
                                    Connector::Noon,
                                    Connector::Novalnet,
                                    Connector::Payme,
                                    Connector::Stripe,
                                    Connector::Bankofamerica,
                                    Connector::Cybersource,
                                    Connector::Wellsfargo,
                                ]),
                            },
                        ),
                        (
                            enums::PaymentMethodType::Debit,
                            SupportedConnectorsForMandate {
                                connector_list: HashSet::from([
                                    Connector::Aci,
                                    Connector::Adyen,
                                    Connector::Authorizedotnet,
                                    Connector::Globalpay,
                                    Connector::Worldpay,
                                    Connector::Fiuu,
                                    Connector::Multisafepay,
                                    Connector::Nexinets,
                                    Connector::Noon,
                                    Connector::Novalnet,
                                    Connector::Payme,
                                    Connector::Stripe,
                                ]),
                            },
                        ),
                    ])),
                ),
            ])),
            update_mandate_supported: SupportedPaymentMethodsForMandate(HashMap::default()),
        }
    }
}


pub struct BarclaycardAuthValidateRequest {
    payment_information: PaymentInformation,
    client_reference_information: ClientReferenceInformation,
    consumer_authentication_information: BarclaycardConsumerAuthInformationValidateRequest,
    order_information: OrderInformation,
}


pub fn extract_merchant_details_url(self, paypal_base_url: &str) -> RouterResult<String> {
        self.links
            .first()
            .and_then(|link| link.href.strip_prefix('/'))
            .map(|link| format!("{paypal_base_url}{link}"))
            .ok_or(ApiErrorResponse::InternalServerError)
            .attach_printable("Merchant details not received in onboarding status")
    }


File: crates/common_utils/src/types/user/core.rs
 Public structs: 1

use diesel::{deserialize::FromSqlRow, expression::AsExpression};

use crate::id_type;

/// Struct for lineageContext
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, AsExpression, FromSqlRow)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct LineageContext {
    /// user_id: String
    pub user_id: String,

    /// merchant_id: MerchantId
    pub merchant_id: id_type::MerchantId,

    /// role_id: String
    pub role_id: String,

    /// org_id: OrganizationId
    pub org_id: id_type::OrganizationId,

    /// profile_id: ProfileId
    pub profile_id: id_type::ProfileId,

    /// tenant_id: TenantId
    pub tenant_id: id_type::TenantId,
}

crate::impl_to_sql_from_sql_json!(LineageContext);


pub struct GooglePayData {
    googlepay: GooglePayDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}


pub fn get_error_reason(
    error_info: Option<String>,
    detailed_error_info: Option<String>,
    avs_error_info: Option<String>,
) -> Option<String> {
    match (error_info, detailed_error_info, avs_error_info) {
        (Some(message), Some(details), Some(avs_message)) => Some(format!(
            "{message}, detailed_error_information: {details}, avs_message: {avs_message}",
        )),
        (Some(message), Some(details), None) => {
            Some(format!("{message}, detailed_error_information: {details}"))
        }
        (Some(message), None, Some(avs_message)) => {
            Some(format!("{message}, avs_message: {avs_message}"))
        }
        (None, Some(details), Some(avs_message)) => {
            Some(format!("{details}, avs_message: {avs_message}"))
        }
        (Some(message), None, None) => Some(message),
        (None, Some(details), None) => Some(details),
        (None, None, Some(avs_message)) => Some(avs_message),
        (None, None, None) => None,
    }
}


pub async fn get_store(
    config: &Settings,
    tenant: &dyn TenantConfig,
    cache_store: Arc<RedisStore>,
    test_transaction: bool,
) -> StorageResult<Store> {
    let master_config = config.master_database.clone().into_inner();

    #[cfg(feature = "olap")]
    let replica_config = config.replica_database.clone().into_inner();

    #[allow(clippy::expect_used)]
    let master_enc_key = hex::decode(config.secrets.get_inner().master_enc_key.clone().expose())
        .map(StrongSecret::new)
        .expect("Failed to decode master key from hex");

    #[cfg(not(feature = "olap"))]
    let conf = master_config.into();
    #[cfg(feature = "olap")]
    // this would get abstracted, for all cases
    #[allow(clippy::useless_conversion)]
    let conf = (master_config.into(), replica_config.into());

    let store: RouterStore<StoreType> = if test_transaction {
        RouterStore::test_store(conf, tenant, &config.redis, master_enc_key).await?
    } else {
        RouterStore::from_config(
            conf,
            tenant,
            master_enc_key,
            cache_store,
            storage_impl::redis::cache::IMC_INVALIDATION_CHANNEL,
        )
        .await?
    };

    #[cfg(feature = "kv_store")]
    let store = KVRouterStore::from_store(
        store,
        config.drainer.stream_name.clone(),
        config.drainer.num_partitions,
        config.kv_config.ttl,
        config.kv_config.soft_kill,
    );

    Ok(store)
}


- **connector:**
  - [Worldpay] Currency Unit Conversion ([#2436](https://github.com/juspay/hyperswitch/pull/2436)) ([`b78109b`](https://github.com/juspay/hyperswitch/commit/b78109bc93433e0886b0b8656231899df84da8cf))
  - [noon] use connector_request_reference_id for sync ([#2558](https://github.com/juspay/hyperswitch/pull/2558)) ([`0889a6e`](https://github.com/juspay/hyperswitch/commit/0889a6ed0691abeed7bba44e7024545abcc74aef))
  - [noon] update and add recommended fields  ([#2381](https://github.com/juspay/hyperswitch/pull/2381)) ([`751f16e`](https://github.com/juspay/hyperswitch/commit/751f16eaee254ab8f0068e2e9e81e3e4b7fe133f))
- **worldline:** Use `connector_request_reference_id` as reference to the connector ([#2498](https://github.com/juspay/hyperswitch/pull/2498)) ([`efa5320`](https://github.com/juspay/hyperswitch/commit/efa53204e8ab1ef1192bcdc07ed99306475badbc))

### Revert

- Fix(connector): [noon] sync with reference_id ([#2556](https://github.com/juspay/hyperswitch/pull/2556)) ([`13be4d3`](https://github.com/juspay/hyperswitch/commit/13be4d36eac3d1e17d8ad9b3f3ef8993547f548b))

**Full Changelog:** [`v1.56.0...v1.57.0`](https://github.com/juspay/hyperswitch/compare/v1.56.0...v1.57.0)

- - -

## 1.56.0 (2023-10-11)

### Features

- **connector:**
  - [Volt] Template generation ([#2480](https://github.com/juspay/hyperswitch/pull/2480)) ([`ee321bb`](https://github.com/juspay/hyperswitch/commit/ee321bb82686559643d8c2725b0491997af717b2))
  - [NexiNets] Update connector_response_reference_id as reference to merchant ([#2537](https://github.com/juspay/hyperswitch/pull/2537)) ([`2f6c00a`](https://github.com/juspay/hyperswitch/commit/2f6c00a1fd853876333608a7d1fa6b488c3001d3))
  - [Authorizedotnet] use connector_response_reference_id as reference to merchant ([#2497](https://github.com/juspay/hyperswitch/pull/2497)) ([`62638c4`](https://github.com/juspay/hyperswitch/commit/62638c4230bfd149c43c2805cbad0ce9be5386b3))
- **router:** Change temp locker config as enable only ([#2522](https://github.com/juspay/hyperswitch/pull/2522)) ([`7acf101`](https://github.com/juspay/hyperswitch/commit/7acf10101435ab97d93490e19eaac5373d34f531))

### Refactors

- Delete requires cvv config when merchant account is deleted ([#2525](https://github.com/juspay/hyperswitch/pull/2525)) ([`b968552`](https://github.com/juspay/hyperswitch/commit/b9685521735956659c50bc2e1c15b08cb9952aee))

### Testing

- **postman:**
  - Add proper `customer_id` in payment method create api ([#2548](https://github.com/juspay/hyperswitch/pull/2548)) ([`7994a12`](https://github.com/juspay/hyperswitch/commit/7994a1259c5852ba4ebabb906bef963c6cf81bc9))
  - Update postman collection files ([`7c561d5`](https://github.com/juspay/hyperswitch/commit/7c561d57767001e755fc9abfc32352ffdc9aacea))

### Miscellaneous Tasks

- **CODEOWNERS:** Update CODEOWNERS ([#2541](https://github.com/juspay/hyperswitch/pull/2541)) ([`d9fb5d4`](https://github.com/juspay/hyperswitch/commit/d9fb5d4a52f44809ab4a1576a99e97b4c8b8c41b))

**Full Changelog:** [`v1.55.0...v1.56.0`](https://github.com/juspay/hyperswitch/compare/v1.55.0...v1.56.0)

- - -

## 1.55.0 (2023-10-10)

### Features

- **connector:**
  - [Multisafepay] Use connector_request_reference_id as reference to the connector ([#2503](https://github.com/juspay/hyperswitch/pull/2503)) ([`c34f1bf`](https://github.com/juspay/hyperswitch/commit/c34f1bf36ffb3a3533dd51ac87e7f66ab0dcce79))
  - [GlobalPayments] Introduce connector_request_reference_id for GlobalPayments ([#2519](https://github.com/juspay/hyperswitch/pull/2519)) ([`116139b`](https://github.com/juspay/hyperswitch/commit/116139ba7ae6878b7018068b0cb8303a8e8d1f7a))
  - [Airwallex] Use connector_request_reference_id as merchant reference id #2291 ([#2516](https://github.com/juspay/hyperswitch/pull/2516)) ([`6e89e41`](https://github.com/juspay/hyperswitch/commit/6e89e4103da4ecf6d7f06f7a9ec7da64eb493a6e))
- **trace:** Add optional sampling behaviour for routes ([#2511](https://github.com/juspay/hyperswitch/pull/2511)) ([`ec51e48`](https://github.com/juspay/hyperswitch/commit/ec51e48402da63e1250328485095b8665d7eca65))
- Gracefully shutdown drainer if redis goes down ([#2391](https://github.com/juspay/hyperswitch/pull/2391)) ([`2870af1`](https://github.com/juspay/hyperswitch/commit/2870af1286e897be0d40c014bc5742eafc6795db))
- Kv for reverse lookup ([#2445](https://github.com/juspay/hyperswitch/pull/2445)) ([`13aaf96`](https://github.com/juspay/hyperswitch/commit/13aaf96db0f62dc7a706ba2ba230912ee7ef7a68))
- Add x-hs-latency header for application overhead measurement ([#2486](https://github.com/juspay/hyperswitch/pull/2486)) ([`cf0db35`](https://github.com/juspay/hyperswitch/commit/cf0db35923d39caca9bf267b7d87a3f215884b66))

### Bug Fixes

- **connector:**
  - [Airwallex] convert expiry year to four digit ([#2527](https://github.com/juspay/hyperswitch/pull/2527)) ([`4b0fa12`](https://github.com/juspay/hyperswitch/commit/4b0fa1295ca8f4e611b65fbf2458c38b89303d3b))
  - [noon] add missing response status ([#2528](https://github.com/juspay/hyperswitch/pull/2528)) ([`808ee45`](https://github.com/juspay/hyperswitch/commit/808ee45556f90b1c1360a3edbffe9ba3603439d4))

### Refactors

- **payment_methods:** Added mca_id in bank details ([#2495](https://github.com/juspay/hyperswitch/pull/2495)) ([`ac3c500`](https://github.com/juspay/hyperswitch/commit/ac3c5008f80172a575f2fb08b7a5e78016ce7595))
- **test_utils:** Refactor `test_utils` crate and add `folder` support with updated documentation ([#2487](https://github.com/juspay/hyperswitch/pull/2487)) ([`6b52ac3`](https://github.com/juspay/hyperswitch/commit/6b52ac3d398d5a180c1dc67c5b53702ad01a0773))

### Miscellaneous Tasks

- [GOCARDLESS] env changes for becs and sepa mandates ([#2535](https://github.com/juspay/hyperswitch/pull/2535)) ([`4f5a383`](https://github.com/juspay/hyperswitch/commit/4f5a383bab567a1b46b2d6990c0c23ed60f1201b))

**Full Changelog:** [`v1.54.0...v1.55.0`](https://github.com/juspay/hyperswitch/compare/v1.54.0...v1.55.0)

- - -

## 1.54.0 (2023-10-09)

### Features

- **connector:**
  - [Fiserv] update connector_response_reference_id in transformers ([#2489](https://github.com/juspay/hyperswitch/pull/2489)) ([`4eb7003`](https://github.com/juspay/hyperswitch/commit/4eb70034336e5ff42c9eea912d940ea04cae9326))
  - [Nuvei] Use "connector_request_reference_id" for as "attempt_id" to improve consistency in transmitting payment information ([#2493](https://github.com/juspay/hyperswitch/pull/2493)) ([`17393f5`](https://github.com/juspay/hyperswitch/commit/17393f5be3e9027fedf9466c6401754f3c4d6b99))
- **kv:** Add kv wrapper for executing kv tasks ([#2384](https://github.com/juspay/hyperswitch/pull/2384)) ([`8b50997`](https://github.com/juspay/hyperswitch/commit/8b50997e56307507be101c562aa70d0a9b429137))
- **process_tracker:** Make long standing payments failed ([#2380](https://github.com/juspay/hyperswitch/pull/2380)) ([`73dfc31`](https://github.com/juspay/hyperswitch/commit/73dfc31f9d16d2cf71de8433fb630bea941a7020))

### Bug Fixes

- Add release feature to drianer ([#2507](https://github.com/juspay/hyperswitch/pull/2507)) ([`224b83c`](https://github.com/juspay/hyperswitch/commit/224b83c51d53fb1ca9ae11ff2f60b7b6cc807fc8))

### Refactors

- Disable color in reports in json format ([#2509](https://github.com/juspay/hyperswitch/pull/2509)) ([`aa176c7`](https://github.com/juspay/hyperswitch/commit/aa176c7c5d79f68c8bd97a3248fd4d40e937a3ce))

### Miscellaneous Tasks

- Address Rust 1.73 clippy lints ([#2474](https://github.com/juspay/hyperswitch/pull/2474)) ([`e02838e`](https://github.com/juspay/hyperswitch/commit/e02838eb5d3da97ef573926ded4a318ed24b6f1c))

**Full Changelog:** [`v1.53.0...v1.54.0`](https://github.com/juspay/hyperswitch/compare/v1.53.0...v1.54.0)

- - -

## 1.53.0 (2023-10-09)

### Features

- **connector:**
  - [Braintree] implement dispute webhook  ([#2031](https://github.com/juspay/hyperswitch/pull/2031)) ([`eeccd10`](https://github.com/juspay/hyperswitch/commit/eeccd106ae569bd60011ed71495d7978998161f8))
  - [Paypal] Implement 3DS for Cards ([#2443](https://github.com/juspay/hyperswitch/pull/2443)) ([`d95a64d`](https://github.com/juspay/hyperswitch/commit/d95a64d6c9b870bdc38aa091cf9bf660b1ea404e))
  - [Cybersource] Use connector_response_reference_id as reference to merchant  ([#2470](https://github.com/juspay/hyperswitch/pull/2470)) ([`a2dfc48`](https://github.com/juspay/hyperswitch/commit/a2dfc48318363db051f311ee7f911de0db0eb868))
  - [Coinbase] Add order id as the reference id  ([#2469](https://github.com/juspay/hyperswitch/pull/2469)) ([`9c2fff5`](https://github.com/juspay/hyperswitch/commit/9c2fff5ab44cdd4f285b6d1437f37869b517963e))
  - [Multisafepay] Use transaction_id as reference to transaction ([#2451](https://github.com/juspay/hyperswitch/pull/2451)) ([`ba2efac`](https://github.com/juspay/hyperswitch/commit/ba2efac4fa2af22f81b0841350a334bc36e91022))

### Bug Fixes

- Add startup config log to drainer ([#2482](https://github.com/juspay/hyperswitch/pull/2482)) ([`5038234`](https://github.com/juspay/hyperswitch/commit/503823408b782968fb59f6ff5d7df417b9aa7dbe))
- Fetch data directly from DB in OLAP functions ([#2475](https://github.com/juspay/hyperswitch/pull/2475)) ([`12b5341`](https://github.com/juspay/hyperswitch/commit/12b534197276ccc4aa9575e6b518bcc50b597bee))

### Refactors

- **connector:** [trustpay] refactor trustpay and handled variants errors ([#2484](https://github.com/juspay/hyperswitch/pull/2484)) ([`3f1e7c2`](https://github.com/juspay/hyperswitch/commit/3f1e7c2152a839a6fe69f60b906277ca831e7611))
- **merchant_account:** Make `organization_id` as mandatory ([#2458](https://github.com/juspay/hyperswitch/pull/2458)) ([`53b4816`](https://github.com/juspay/hyperswitch/commit/53b4816d27fe7794cb482887ed17ddb4386bd2f7))

### Miscellaneous Tasks

- Env changes for gocardless mandate ([#2485](https://github.com/juspay/hyperswitch/pull/2485)) ([`65ca5f1`](https://github.com/juspay/hyperswitch/commit/65ca5f12da54715e5db785d122e2ec9714147c68))

**Full Changelog:** [`v1.52.0...v1.53.0`](https://github.com/juspay/hyperswitch/compare/v1.52.0...v1.53.0)

- - -

## 1.52.0 (2023-10-06)

### Features

- **connector:**
  - [Forte] Use connector_response_reference_id as reference to merchant ([#2456](https://github.com/juspay/hyperswitch/pull/2456)) ([`cc7e90f`](https://github.com/juspay/hyperswitch/commit/cc7e90f2293f27b74b14669a0c2d5bd6d45c4d99))
  - [PayU] Use connector_response_response_id as reference to merchant ([#2452](https://github.com/juspay/hyperswitch/pull/2452)) ([`e24897c`](https://github.com/juspay/hyperswitch/commit/e24897cd5d3859124636760a4eb42ee007f00c3e))
  - [Gocardless] Implement mandate flow ([#2461](https://github.com/juspay/hyperswitch/pull/2461)) ([`4149965`](https://github.com/juspay/hyperswitch/commit/414996592b3016bfa9f3399319c6e02ccd333c68))
  - [Gocardless] Add mandate webhoooks ([#2468](https://github.com/juspay/hyperswitch/pull/2468)) ([`8d53c66`](https://github.com/juspay/hyperswitch/commit/8d53c663a5e25817d1facda3352f84f1435efdee))
  - [Noon] Use connector_request_reference_id as Order reference ([#2466](https://github.com/juspay/hyperswitch/pull/2466)) ([`2897b6e`](https://github.com/juspay/hyperswitch/commit/2897b6ecd1a357bae93ca22fe9e7aeed18738b95))
- **core:** Add surcharge_details field to ResponsePaymentMethodTypes struct ([#2435](https://github.com/juspay/hyperswitch/pull/2435)) ([`3f0d927`](https://github.com/juspay/hyperswitch/commit/3f0d927cb8db503c4dede98c691c1b7e6ebd441a))
- **router:** Add mandates incoming webhooks flow ([#2464](https://github.com/juspay/hyperswitch/pull/2464)) ([`1cf8b6c`](https://github.com/juspay/hyperswitch/commit/1cf8b6cf53ee5fdde9a7a3996e5a5e5c5b8341c6))

### Bug Fixes

- Update connector_mandate_id column in generate mandate flow ([#2472](https://github.com/juspay/hyperswitch/pull/2472)) ([`61288d5`](https://github.com/juspay/hyperswitch/commit/61288d541f654bcb102465e4da9883aaaac43f5b))

### Refactors

- **connector:** [nmi] refactor nmi and handled variants errors ([#2463](https://github.com/juspay/hyperswitch/pull/2463)) ([`f364a06`](https://github.com/juspay/hyperswitch/commit/f364a069b90dd63a28cf25457b2cd4fda0829a8b))
- Add support for passing context generic to api calls ([#2433](https://github.com/juspay/hyperswitch/pull/2433)) ([`601c174`](https://github.com/juspay/hyperswitch/commit/601c1744b6f15eb14ecfa3edede3159c32c53492))

**Full Changelog:** [`v1.51.1...v1.52.0`](https://github.com/juspay/hyperswitch/compare/v1.51.1...v1.52.0)

- - -

## 1.51.1 (2023-10-05)

### Bug Fixes

- **router:** Make payment type optional in payments request ([#2465](https://github.com/juspay/hyperswitch/pull/2465)) ([`b5cc748`](https://github.com/juspay/hyperswitch/commit/b5cc7483f99dcd995b9022d21c94f2f9710ea7fe))

### Refactors

- **router:**
  - Renamed Verify flow to SetupMandate ([#2455](https://github.com/juspay/hyperswitch/pull/2455)) ([`80f3b1e`](https://github.com/juspay/hyperswitch/commit/80f3b1edaeae9a13ea291a0315f1be2686336914))
  - Remove the payment type column in payment intent ([#2462](https://github.com/juspay/hyperswitch/pull/2462)) ([`980aa44`](https://github.com/juspay/hyperswitch/commit/980aa448634de86f11fb67aabefc15884f1b8ced))

### Miscellaneous Tasks

- Fix the failing formatting check for external contributors ([#2467](https://github.com/juspay/hyperswitch/pull/2467)) ([`bb2ba08`](https://github.com/juspay/hyperswitch/commit/bb2ba0815330578295de8036ea1a5e6d66a36277))

**Full Changelog:** [`v1.51.0...v1.51.1`](https://github.com/juspay/hyperswitch/compare/v1.51.0...v1.51.1)

- - -

## 1.51.0 (2023-10-05)

### Features

- **connector:**
  - [Noon] Use connector_response_reference_id as reference ([#2442](https://github.com/juspay/hyperswitch/pull/2442)) ([`688557e`](https://github.com/juspay/hyperswitch/commit/688557ef95826622fe87a4de1dfbc09446496686))
  - [Opayo] Add connector id ([#2418](https://github.com/juspay/hyperswitch/pull/2418)) ([`8e51073`](https://github.com/juspay/hyperswitch/commit/8e51073c837909838b92a9eadea32e5a577e2b54))
- **payment_methods:** Bank details support for payment method data in pmt ([#2385](https://github.com/juspay/hyperswitch/pull/2385)) ([`e86c032`](https://github.com/juspay/hyperswitch/commit/e86c0325f51d06ecfcbc810f3320c97850716825))
- **router:** Add support for payment_type field in payment intent ([#2448](https://github.com/juspay/hyperswitch/pull/2448)) ([`f116728`](https://github.com/juspay/hyperswitch/commit/f116728d1cba458a1e184c2fdf5a1cc012430c35))

### Bug Fixes

- **connector:** Use enum to deserialize latest_charge in stripe psync response ([#2444](https://github.com/juspay/hyperswitch/pull/2444)) ([`05ee47a`](https://github.com/juspay/hyperswitch/commit/05ee47a6e90bd68a0faa6dcc381c48a1f0f274d8))
- **payments:** Move validations of payment intent before attempt ([#2440](https://github.com/juspay/hyperswitch/pull/2440)) ([`7fb5c04`](https://github.com/juspay/hyperswitch/commit/7fb5c044bc9611e375b271065859308792773f30))
- Return appropriate error message during webhook call for invalid merchant_secret adyen ([#2450](https://github.com/juspay/hyperswitch/pull/2450)) ([`db7f9fa`](https://github.com/juspay/hyperswitch/commit/db7f9fa801d2bacf3abda6cc447220d254f56382))

### Testing

- **postman:** Update postman collection files ([`a9221d4`](https://github.com/juspay/hyperswitch/commit/a9221d44d192a3baadf9978d517e8153ef7a739a))

**Full Changelog:** [`v1.50.0...v1.51.0`](https://github.com/juspay/hyperswitch/compare/v1.50.0...v1.51.0)

- - -

## 1.50.0 (2023-10-04)

### Features

- **connector:**
  - [Stax] Use connector_response_reference_id as reference to merchant ([#2415](https://github.com/juspay/hyperswitch/pull/2415)) ([`099b241`](https://github.com/juspay/hyperswitch/commit/099b241096c69879a805ca81b1c5a23118e10b52))
  - [PowerTranz] Use connector_response_reference_id as reference to merchant ([#2413](https://github.com/juspay/hyperswitch/pull/2413)) ([`0d703c7`](https://github.com/juspay/hyperswitch/commit/0d703c7ab85c68f433767a70a0feabe8daa4f24c))
  - [Payeezy] Use connector_response_reference_id as reference to merchant ([#2410](https://github.com/juspay/hyperswitch/pull/2410)) ([`485c09d`](https://github.com/juspay/hyperswitch/commit/485c09d16743d73b446d6313f0ee6462c8a77028))
  - [Square] Use reference_id as reference to merchant ([#2434](https://github.com/juspay/hyperswitch/pull/2434)) ([`591c9b7`](https://github.com/juspay/hyperswitch/commit/591c9b70d9b7f8df5d7f5d2cb2d19cfaa1457fe1))
- **router:**
  - Remove unnecessary lookups in refund and payment_attempt kv flow ([#2425](https://github.com/juspay/hyperswitch/pull/2425)) ([`f720aec`](https://github.com/juspay/hyperswitch/commit/f720aecf1fb676cec71e636b877a46f9791d713a))
  - [OpenNode] response reference id ([#2416](https://github.com/juspay/hyperswitch/pull/2416)) ([`3bfea72`](https://github.com/juspay/hyperswitch/commit/3bfea72df34f4ce0ffdb61e49960fdf09b96eb5a))
  - Add profile id and extra filters in lists ([#2379](https://github.com/juspay/hyperswitch/pull/2379)) ([`ab2cde7`](https://github.com/juspay/hyperswitch/commit/ab2cde799371a66eb045cf8b20431b3b108dac44))

### Bug Fixes

- **CI:** Fix spell check for CI pull request ([#2439](https://github.com/juspay/hyperswitch/pull/2439)) ([`04f2e11`](https://github.com/juspay/hyperswitch/commit/04f2e11cd4f3fd327408cddec36ccf4fb486b935))
- **router:** Merchant account delete does not delete the merchant_key_store ([#2367](https://github.com/juspay/hyperswitch/pull/2367)) ([`35f7ce0`](https://github.com/juspay/hyperswitch/commit/35f7ce0f4d9e16184e2bb94360d3ced60f8b5af2))

### Refactors

- **config:** Update payment method filter for apple pay ([#2423](https://github.com/juspay/hyperswitch/pull/2423)) ([`d177b4d`](https://github.com/juspay/hyperswitch/commit/d177b4d94f08fb8ef44b5c07ec1bdc771baa016d))
- **payment_methods:** Add `requires_cvv` config while creating merchant account ([#2431](https://github.com/juspay/hyperswitch/pull/2431)) ([`6e5ab0d`](https://github.com/juspay/hyperswitch/commit/6e5ab0d121d6345f18bccc7f917064caa2737475))


impl RefundSync for Threedsecureio {}


impl api::PaymentToken for Bankofamerica {}

impl api::PaymentToken for for Bankofamerica


pub struct PaystackPaymentsRequest {
    amount: MinorUnit,
    currency: Currency,
    email: Email,
    eft: PaystackEftProvider,
}


        }
    }
}

#[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "PascalCase")]
pub struct WebhookReferences {
    pub merchant_reference: Option<String>,
    pub payment_id: Option<String>,
    pub payment_request_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct WebhookAmount {
    pub amount: FloatMajorUnit,
    pub currency: enums::Currency,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct WebhookPaymentInformation {
    pub credit_debit_indicator: CreditDebitIndicator,
    pub references: WebhookReferences,
    pub status: WebhookStatus,
    pub amount: WebhookAmount,
    pub status_reason_information: Option<StatusReasonInformation>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct TrustpayWebhookResponse {
    pub payment_information: WebhookPaymentInformation,
    pub signature: String,
}

impl From<Errors> for utils::ErrorCodeAndMessage {
    fn from(error: Errors) -> Self {
        Self {
            error_code: error.code.to_string(),
            error_message: error.description,
        }
    }
}


impl api::RefundSync for Nmi {}


pub struct ExtendedCardInfoChoice {
    pub enabled: bool,
}


pub fn to_merchant_category_name(&self) -> MerchantCategory {
        match self {
            Self::Mcc5411 => MerchantCategory::GroceryStoresSupermarkets,
            Self::Mcc7011 => MerchantCategory::LodgingHotelsMotelsResorts,
            Self::Mcc0763 => MerchantCategory::AgriculturalCooperatives,
            Self::Mcc8111 => MerchantCategory::AttorneysLegalServices,
            Self::Mcc5021 => MerchantCategory::OfficeAndCommercialFurniture,
            Self::Mcc4816 => MerchantCategory::ComputerNetworkInformationServices,
            Self::Mcc5661 => MerchantCategory::ShoeStores,
        }
    }


pub struct FacilitapayPaymentsResponse {
    pub data: TransactionData,
}


Documentation: api-reference/v1/blocklist/post-blocklist.mdx
# Type: Doc File

---
openapi: post /blocklist
---


impl AcceptDispute for Worldpayvantiv {}

impl AcceptDispute for for Worldpayvantiv


pub async fn get_single_merchant_id(
    state: &SessionState,
    user_role: &UserRole,
    org_id: &id_type::OrganizationId,
) -> UserResult<id_type::MerchantId> {
    let (_, entity_type) = user_role
        .get_entity_id_and_type()
        .ok_or(UserErrors::InternalServerError)?;
    match entity_type {
        EntityType::Tenant | EntityType::Organization => Ok(state
            .store
            .list_merchant_accounts_by_organization_id(&state.into(), org_id)
            .await
            .to_not_found_response(UserErrors::InvalidRoleOperationWithMessage(
                "Invalid Org Id".to_string(),
            ))?
            .first()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("No merchants found for org_id")?
            .get_id()
            .clone()),
        EntityType::Merchant | EntityType::Profile => user_role
            .merchant_id
            .clone()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("merchant_id not found"),
    }
}


pub async fn get_global_search_results(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetGlobalSearchRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetGlobalSearchResults;
        Box::pin(api::server_wrap(
            flow,
            state.clone(),
            &req,
            json_payload.into_inner(),
            |state, auth: UserFromToken, req, _| async move {
                let role_id = auth.role_id;
                let role_info = RoleInfo::from_role_id_org_id_tenant_id(
                    &state,
                    &role_id,
                    &auth.org_id,
                    auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id),
                )
                .await
                .change_context(UserErrors::InternalServerError)
                .change_context(OpenSearchError::UnknownError)?;
                let permission_groups = role_info.get_permission_groups();
                if !permission_groups.contains(&common_enums::PermissionGroup::OperationsView) {
                    return Err(OpenSearchError::AccessForbiddenError)?;
                }
                let user_roles: HashSet<UserRole> = match role_info.get_entity_type() {
                    EntityType::Tenant => state
                        .global_store
                        .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                            user_id: &auth.user_id,
                            tenant_id: auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id),
                            org_id: None,
                            merchant_id: None,
                            profile_id: None,
                            entity_id: None,
                            version: None,
                            status: None,
                            limit: None,
                        })
                        .await
                        .change_context(UserErrors::InternalServerError)
                        .change_context(OpenSearchError::UnknownError)?
                        .into_iter()
                        .collect(),
                    EntityType::Organization | EntityType::Merchant | EntityType::Profile => state
                        .global_store
                        .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                            user_id: &auth.user_id,
                            tenant_id: auth.tenant_id.as_ref().unwrap_or(&state.tenant.tenant_id),
                            org_id: Some(&auth.org_id),
                            merchant_id: None,
                            profile_id: None,
                            entity_id: None,
                            version: None,
                            status: None,
                            limit: None,
                        })
                        .await
                        .change_context(UserErrors::InternalServerError)
                        .change_context(OpenSearchError::UnknownError)?
                        .into_iter()
                        .collect(),
                };

                let state = Arc::new(state);
                let role_info_map: HashMap<String, RoleInfo> = user_roles
                    .iter()
                    .map(|user_role| {
                        let state = Arc::clone(&state);
                        let role_id = user_role.role_id.clone();
                        let org_id = user_role.org_id.clone().unwrap_or_default();
                        let tenant_id = &user_role.tenant_id;
                        async move {
                            RoleInfo::from_role_id_org_id_tenant_id(
                                &state, &role_id, &org_id, tenant_id,
                            )
                            .await
                            .change_context(UserErrors::InternalServerError)
                            .change_context(OpenSearchError::UnknownError)
                            .map(|role_info| (role_id, role_info))
                        }
                    })
                    .collect::<FuturesUnordered<_>>()
                    .collect::<Vec<_>>()
                    .await
                    .into_iter()
                    .collect::<Result<HashMap<_, _>, _>>()?;

                let filtered_user_roles: Vec<&UserRole> = user_roles
                    .iter()
                    .filter(|user_role| {
                        let user_role_id = &user_role.role_id;
                        if let Some(role_info) = role_info_map.get(user_role_id) {
                            let permissions = role_info.get_permission_groups();
                            permissions.contains(&common_enums::PermissionGroup::OperationsView)
                        } else {
                            false
                        }
                    })
                    .collect();

                let search_params: Vec<AuthInfo> = filtered_user_roles
                    .iter()
                    .filter_map(|user_role| {
                        user_role
                            .get_entity_id_and_type()
                            .and_then(|(_, entity_type)| match entity_type {
                                EntityType::Profile => Some(AuthInfo::ProfileLevel {
                                    org_id: user_role.org_id.clone()?,
                                    merchant_id: user_role.merchant_id.clone()?,
                                    profile_ids: vec![user_role.profile_id.clone()?],
                                }),
                                EntityType::Merchant => Some(AuthInfo::MerchantLevel {
                                    org_id: user_role.org_id.clone()?,
                                    merchant_ids: vec![user_role.merchant_id.clone()?],
                                }),
                                EntityType::Organization => Some(AuthInfo::OrgLevel {
                                    org_id: user_role.org_id.clone()?,
                                }),
                                EntityType::Tenant => Some(AuthInfo::OrgLevel {
                                    org_id: auth.org_id.clone(),
                                }),
                            })
                    })
                    .collect();

                analytics::search::msearch_results(
                    state
                        .opensearch_client
                        .as_ref()
                        .ok_or_else(|| error_stack::report!(OpenSearchError::NotEnabled))?,
                    req,
                    search_params,
                    SEARCH_INDEXES.to_vec(),
                )
                .await
                .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub struct DebitRoutingAccumulator {
    pub transaction_count: u64,
    pub savings_amount: u64,
}


impl api::PaymentVoid for Opayo {}


pub struct AddVaultRequest<D> {
    pub entity_id: id_type::GlobalCustomerId,
    pub vault_id: domain::VaultId,
    pub data: D,
    pub ttl: i64,
}


pub struct PaypalMandateStruct {
    experience_context: Option<ContextStruct>,
    usage_type: UsageType,
}


pub struct Signifyd;


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


pub struct MerchantConnectorAccountUpdateInternal {
    pub connector_type: Option<storage_enums::ConnectorType>,
    pub connector_name: Option<String>,
    pub connector_account_details: Option<Encryption>,
    pub connector_label: Option<String>,
    pub test_mode: Option<bool>,
    pub disabled: Option<bool>,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub payment_methods_enabled: Option<Vec<pii::SecretSerdeValue>>,
    pub frm_configs: Option<pii::SecretSerdeValue>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: Option<time::PrimitiveDateTime>,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: Option<storage_enums::ConnectorStatus>,
    pub connector_wallets_details: Option<Encryption>,
    pub additional_merchant_data: Option<Encryption>,
}


File: crates/router/src/core/revenue_recovery/transformers.rs

use common_enums::AttemptStatus;
use masking::PeekInterface;

use crate::{
    core::revenue_recovery::types::RevenueRecoveryPaymentsAttemptStatus,
    types::transformers::ForeignFrom,
};

impl ForeignFrom<AttemptStatus> for RevenueRecoveryPaymentsAttemptStatus {
    fn foreign_from(s: AttemptStatus) -> Self {
        match s {
            AttemptStatus::Authorized
            | AttemptStatus::Charged
            | AttemptStatus::AutoRefunded
            | AttemptStatus::PartiallyAuthorized => Self::Succeeded,

            AttemptStatus::Started
            | AttemptStatus::AuthenticationSuccessful
            | AttemptStatus::Authorizing
            | AttemptStatus::CodInitiated
            | AttemptStatus::VoidInitiated
            | AttemptStatus::CaptureInitiated
            | AttemptStatus::Pending => Self::Processing,

            AttemptStatus::AuthenticationFailed
            | AttemptStatus::AuthorizationFailed
            | AttemptStatus::VoidFailed
            | AttemptStatus::RouterDeclined
            | AttemptStatus::CaptureFailed
            | AttemptStatus::Failure => Self::Failed,

            AttemptStatus::Voided
            | AttemptStatus::VoidedPostCharge
            | AttemptStatus::ConfirmationAwaited
            | AttemptStatus::PartialCharged
            | AttemptStatus::PartialChargedAndChargeable
            | AttemptStatus::PaymentMethodAwaited
            | AttemptStatus::AuthenticationPending
            | AttemptStatus::DeviceDataCollectionPending
            | AttemptStatus::Unresolved
            | AttemptStatus::IntegrityFailure
            | AttemptStatus::Expired => Self::InvalidStatus(s.to_string()),
        }
    }
}

impl ForeignFrom<api_models::payments::RecoveryPaymentsCreate>
    for hyperswitch_domain_models::revenue_recovery::RevenueRecoveryInvoiceData
{
    fn foreign_from(data: api_models::payments::RecoveryPaymentsCreate) -> Self {
        Self {
            amount: data.amount_details.order_amount().into(),
            currency: data.amount_details.currency(),
            merchant_reference_id: data.merchant_reference_id,
            billing_address: data.billing,
            retry_count: None,
            next_billing_at: None,
            billing_started_at: data.billing_started_at,
        }
    }
}

impl ForeignFrom<&api_models::payments::RecoveryPaymentsCreate>
    for hyperswitch_domain_models::revenue_recovery::RevenueRecoveryAttemptData
{
    fn foreign_from(data: &api_models::payments::RecoveryPaymentsCreate) -> Self {
        Self {
            amount: data.amount_details.order_amount().into(),
            currency: data.amount_details.currency(),
            merchant_reference_id: data.merchant_reference_id.to_owned(),
            connector_transaction_id: data.connector_transaction_id.as_ref().map(|txn_id| {
                common_utils::types::ConnectorTransactionId::TxnId(txn_id.peek().to_string())
            }),
            error_code: data.error.as_ref().map(|error| error.code.clone()),
            error_message: data.error.as_ref().map(|error| error.message.clone()),
            processor_payment_method_token: data
                .payment_method_data
                .primary_processor_payment_method_token
                .peek()
                .to_string(),
            connector_customer_id: data.connector_customer_id.peek().to_string(),
            connector_account_reference_id: data
                .payment_merchant_connector_id
                .get_string_repr()
                .to_string(),
            transaction_created_at: data.transaction_created_at.to_owned(),
            status: data.attempt_status,
            payment_method_type: data.payment_method_type,
            payment_method_sub_type: data.payment_method_sub_type,
            network_advice_code: data
                .error
                .as_ref()
                .and_then(|error| error.network_advice_code.clone()),
            network_decline_code: data
                .error
                .as_ref()
                .and_then(|error| error.network_decline_code.clone()),
            network_error_message: data
                .error
                .as_ref()
                .and_then(|error| error.network_error_message.clone()),
            // retry count will be updated whenever there is new attempt is created.
            retry_count: None,
            invoice_next_billing_time: None,
            invoice_billing_started_at_time: data.billing_started_at,
            card_info: data
                .payment_method_data
                .additional_payment_method_info
                .clone(),
            charge_id: None,
        }
    }
}


File: crates/router/src/utils/user_role.rs
 Public functions: 15

use std::{
    cmp,
    collections::{HashMap, HashSet},
};

use api_models::user_role::role as role_api;
use common_enums::{EntityType, ParentGroup, PermissionGroup};
use common_utils::id_type;
use diesel_models::{
    enums::{UserRoleVersion, UserStatus},
    role::ListRolesByEntityPayload,
    user_role::{UserRole, UserRoleUpdate},
};
use error_stack::{report, Report, ResultExt};
use router_env::logger;
use storage_impl::errors::StorageError;
use strum::IntoEnumIterator;

use crate::{
    consts,
    core::errors::{UserErrors, UserResult},
    db::{
        errors::StorageErrorExt,
        user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload},
    },
    routes::SessionState,
    services::authorization::{
        self as authz,
        permission_groups::{ParentGroupExt, PermissionGroupExt},
        roles,
    },
    types::domain,
};

pub fn validate_role_groups(groups: &[PermissionGroup]) -> UserResult<()> {
    if groups.is_empty() {
        return Err(report!(UserErrors::InvalidRoleOperation))
            .attach_printable("Role groups cannot be empty");
    }

    let unique_groups: HashSet<_> = groups.iter().copied().collect();

    if unique_groups.contains(&PermissionGroup::OrganizationManage)
        || unique_groups.contains(&PermissionGroup::InternalManage)
    {
        return Err(report!(UserErrors::InvalidRoleOperation))
            .attach_printable("Invalid groups present in the custom role");
    }

    if unique_groups.len() != groups.len() {
        return Err(report!(UserErrors::InvalidRoleOperation))
            .attach_printable("Duplicate permission group found");
    }

    Ok(())
}

pub async fn validate_role_name(
    state: &SessionState,
    role_name: &domain::RoleName,
    merchant_id: &id_type::MerchantId,
    org_id: &id_type::OrganizationId,
    tenant_id: &id_type::TenantId,
    profile_id: &id_type::ProfileId,
    entity_type: &EntityType,
) -> UserResult<()> {
    let role_name_str = role_name.clone().get_role_name();

    let is_present_in_predefined_roles = roles::predefined_roles::PREDEFINED_ROLES
        .iter()
        .any(|(_, role_info)| role_info.get_role_name() == role_name_str);

    let entity_type_for_role = match entity_type {
        EntityType::Tenant | EntityType::Organization => ListRolesByEntityPayload::Organization,
        EntityType::Merchant => ListRolesByEntityPayload::Merchant(merchant_id.to_owned()),
        EntityType::Profile => {
            ListRolesByEntityPayload::Profile(merchant_id.to_owned(), profile_id.to_owned())
        }
    };

    let is_present_in_custom_role = match state
        .global_store
        .generic_list_roles_by_entity_type(
            entity_type_for_role,
            false,
            tenant_id.to_owned(),
            org_id.to_owned(),
        )
        .await
    {
        Ok(roles_list) => roles_list
            .iter()
            .any(|role| role.role_name == role_name_str),
        Err(e) => {
            if e.current_context().is_db_not_found() {
                false
            } else {
                return Err(UserErrors::InternalServerError.into());
            }
        }
    };

    if is_present_in_predefined_roles || is_present_in_custom_role {
        return Err(UserErrors::RoleNameAlreadyExists.into());
    }

    Ok(())
}

pub async fn set_role_info_in_cache_by_user_role(
    state: &SessionState,
    user_role: &UserRole,
) -> bool {
    let Some(ref org_id) = user_role.org_id else {
        return false;
    };
    set_role_info_in_cache_if_required(
        state,
        user_role.role_id.as_str(),
        org_id,
        &user_role.tenant_id,
    )
    .await
    .map_err(|e| logger::error!("Error setting permissions in cache {:?}", e))
    .is_ok()
}

pub async fn set_role_info_in_cache_by_role_id_org_id(
    state: &SessionState,
    role_id: &str,
    org_id: &id_type::OrganizationId,
    tenant_id: &id_type::TenantId,
) -> bool {
    set_role_info_in_cache_if_required(state, role_id, org_id, tenant_id)
        .await
        .map_err(|e| logger::error!("Error setting permissions in cache {:?}", e))
        .is_ok()
}

pub async fn set_role_info_in_cache_if_required(
    state: &SessionState,
    role_id: &str,
    org_id: &id_type::OrganizationId,
    tenant_id: &id_type::TenantId,
) -> UserResult<()> {
    if roles::predefined_roles::PREDEFINED_ROLES.contains_key(role_id) {
        return Ok(());
    }

    let role_info =
        roles::RoleInfo::from_role_id_org_id_tenant_id(state, role_id, org_id, tenant_id)
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Error getting role_info from role_id")?;

    authz::set_role_info_in_cache(
        state,
        role_id,
        &role_info,
        i64::try_from(consts::JWT_TOKEN_TIME_IN_SECS)
            .change_context(UserErrors::InternalServerError)?,
    )
    .await
    .change_context(UserErrors::InternalServerError)
    .attach_printable("Error setting permissions in redis")
}

pub async fn update_v1_and_v2_user_roles_in_db(
    state: &SessionState,
    user_id: &str,
    tenant_id: &id_type::TenantId,
    org_id: &id_type::OrganizationId,
    merchant_id: Option<&id_type::MerchantId>,
    profile_id: Option<&id_type::ProfileId>,
    update: UserRoleUpdate,
) -> (
    Result<UserRole, Report<StorageError>>,
    Result<UserRole, Report<StorageError>>,
) {
    let updated_v1_role = state
        .global_store
        .update_user_role_by_user_id_and_lineage(
            user_id,
            tenant_id,
            org_id,
            merchant_id,
            profile_id,
            update.clone(),
            UserRoleVersion::V1,
        )
        .await
        .map_err(|e| {
            logger::error!("Error updating user_role {e:?}");
            e
        });

    let updated_v2_role = state
        .global_store
        .update_user_role_by_user_id_and_lineage(
            user_id,
            tenant_id,
            org_id,
            merchant_id,
            profile_id,
            update,
            UserRoleVersion::V2,
        )
        .await
        .map_err(|e| {
            logger::error!("Error updating user_role {e:?}");
            e
        });

    (updated_v1_role, updated_v2_role)
}

pub async fn get_single_org_id(
    state: &SessionState,
    user_role: &UserRole,
) -> UserResult<id_type::OrganizationId> {
    let (_, entity_type) = user_role
        .get_entity_id_and_type()
        .ok_or(UserErrors::InternalServerError)?;
    match entity_type {
        EntityType::Tenant => Ok(state
            .store
            .list_merchant_and_org_ids(&state.into(), 1, None)
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Failed to get merchants list for org")?
            .pop()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("No merchants to get merchant or org id")?
            .1),
        EntityType::Organization | EntityType::Merchant | EntityType::Profile => user_role
            .org_id
            .clone()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("Org_id not found"),
    }
}

pub async fn get_single_merchant_id(
    state: &SessionState,
    user_role: &UserRole,
    org_id: &id_type::OrganizationId,
) -> UserResult<id_type::MerchantId> {
    let (_, entity_type) = user_role
        .get_entity_id_and_type()
        .ok_or(UserErrors::InternalServerError)?;
    match entity_type {
        EntityType::Tenant | EntityType::Organization => Ok(state
            .store
            .list_merchant_accounts_by_organization_id(&state.into(), org_id)
            .await
            .to_not_found_response(UserErrors::InvalidRoleOperationWithMessage(
                "Invalid Org Id".to_string(),
            ))?
            .first()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("No merchants found for org_id")?
            .get_id()
            .clone()),
        EntityType::Merchant | EntityType::Profile => user_role
            .merchant_id
            .clone()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("merchant_id not found"),
    }
}

pub async fn get_single_profile_id(
    state: &SessionState,
    user_role: &UserRole,
    merchant_id: &id_type::MerchantId,
) -> UserResult<id_type::ProfileId> {
    let (_, entity_type) = user_role
        .get_entity_id_and_type()
        .ok_or(UserErrors::InternalServerError)?;
    match entity_type {
        EntityType::Tenant | EntityType::Organization | EntityType::Merchant => {
            let key_store = state
                .store
                .get_merchant_key_store_by_merchant_id(
                    &state.into(),
                    merchant_id,
                    &state.store.get_master_key().to_vec().into(),
                )
                .await
                .change_context(UserErrors::InternalServerError)?;

            Ok(state
                .store
                .list_profile_by_merchant_id(&state.into(), &key_store, merchant_id)
                .await
                .change_context(UserErrors::InternalServerError)?
                .pop()
                .ok_or(UserErrors::InternalServerError)?
                .get_id()
                .to_owned())
        }
        EntityType::Profile => user_role
            .profile_id
            .clone()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("profile_id not found"),
    }
}

pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite(
    state: &SessionState,
    user_id: &str,
    tenant_id: &id_type::TenantId,
    entity_id: String,
    entity_type: EntityType,
) -> UserResult<
    Option<(
        id_type::OrganizationId,
        Option<id_type::MerchantId>,
        Option<id_type::ProfileId>,
    )>,
> {
    match entity_type {
        EntityType::Tenant => Err(UserErrors::InvalidRoleOperationWithMessage(
            "Tenant roles are not allowed for this operation".to_string(),
        )
        .into()),
        EntityType::Organization => {
            let Ok(org_id) =
                id_type::OrganizationId::try_from(std::borrow::Cow::from(entity_id.clone()))
            else {
                return Ok(None);
            };

            let user_roles = state
                .global_store
                .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                    user_id,
                    tenant_id,
                    org_id: Some(&org_id),
                    merchant_id: None,
                    profile_id: None,
                    entity_id: None,
                    version: None,
                    status: Some(UserStatus::InvitationSent),
                    limit: None,
                })
                .await
                .change_context(UserErrors::InternalServerError)?
                .into_iter()
                .collect::<HashSet<_>>();

            if user_roles.len() > 1 {
                return Ok(None);
            }

            if let Some(user_role) = user_roles.into_iter().next() {
                let (_entity_id, entity_type) = user_role
                    .get_entity_id_and_type()
                    .ok_or(UserErrors::InternalServerError)?;

                if entity_type != EntityType::Organization {
                    return Ok(None);
                }

                return Ok(Some((
                    user_role.org_id.ok_or(UserErrors::InternalServerError)?,
                    None,
                    None,
                )));
            }

            Ok(None)
        }
        EntityType::Merchant => {
            let Ok(merchant_id) = id_type::MerchantId::wrap(entity_id) else {
                return Ok(None);
            };

            let user_roles = state
                .global_store
                .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                    user_id,
                    tenant_id,
                    org_id: None,
                    merchant_id: Some(&merchant_id),
                    profile_id: None,
                    entity_id: None,
                    version: None,
                    status: Some(UserStatus::InvitationSent),
                    limit: None,
                })
                .await
                .change_context(UserErrors::InternalServerError)?
                .into_iter()
                .collect::<HashSet<_>>();

            if user_roles.len() > 1 {
                return Ok(None);
            }

            if let Some(user_role) = user_roles.into_iter().next() {
                let (_entity_id, entity_type) = user_role
                    .get_entity_id_and_type()
                    .ok_or(UserErrors::InternalServerError)?;

                if entity_type != EntityType::Merchant {
                    return Ok(None);
                }

                return Ok(Some((
                    user_role.org_id.ok_or(UserErrors::InternalServerError)?,
                    Some(merchant_id),
                    None,
                )));
            }

            Ok(None)
        }
        EntityType::Profile => {
            let Ok(profile_id) = id_type::ProfileId::try_from(std::borrow::Cow::from(entity_id))
            else {
                return Ok(None);
            };

            let user_roles = state
                .global_store
                .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                    user_id,
                    tenant_id: &state.tenant.tenant_id,
                    org_id: None,
                    merchant_id: None,
                    profile_id: Some(&profile_id),
                    entity_id: None,
                    version: None,
                    status: Some(UserStatus::InvitationSent),
                    limit: None,
                })
                .await
                .change_context(UserErrors::InternalServerError)?
                .into_iter()
                .collect::<HashSet<_>>();

            if user_roles.len() > 1 {
                return Ok(None);
            }

            if let Some(user_role) = user_roles.into_iter().next() {
                let (_entity_id, entity_type) = user_role
                    .get_entity_id_and_type()
                    .ok_or(UserErrors::InternalServerError)?;

                if entity_type != EntityType::Profile {
                    return Ok(None);
                }

                return Ok(Some((
                    user_role.org_id.ok_or(UserErrors::InternalServerError)?,
                    Some(
                        user_role
                            .merchant_id
                            .ok_or(UserErrors::InternalServerError)?,
                    ),
                    Some(profile_id),
                )));
            }

            Ok(None)
        }
    }
}

pub async fn get_single_merchant_id_and_profile_id(
    state: &SessionState,
    user_role: &UserRole,
) -> UserResult<(id_type::MerchantId, id_type::ProfileId)> {
    let org_id = get_single_org_id(state, user_role).await?;
    let merchant_id = get_single_merchant_id(state, user_role, &org_id).await?;
    let profile_id = get_single_profile_id(state, user_role, &merchant_id).await?;

    Ok((merchant_id, profile_id))
}

pub async fn fetch_user_roles_by_payload(
    state: &SessionState,
    payload: ListUserRolesByOrgIdPayload<'_>,
    request_entity_type: Option<EntityType>,
) -> UserResult<HashSet<UserRole>> {
    Ok(state
        .global_store
        .list_user_roles_by_org_id(payload)
        .await
        .change_context(UserErrors::InternalServerError)?
        .into_iter()
        .filter_map(|user_role| {
            let (_entity_id, entity_type) = user_role.get_entity_id_and_type()?;
            request_entity_type
                .map_or(true, |req_entity_type| entity_type == req_entity_type)
                .then_some(user_role)
        })
        .collect::<HashSet<_>>())
}

pub fn get_min_entity(
    user_entity: EntityType,
    filter_entity: Option<EntityType>,
) -> UserResult<EntityType> {
    let Some(filter_entity) = filter_entity else {
        return Ok(user_entity);
    };

    if user_entity < filter_entity {
        return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
            "{user_entity} level user requesting data for {filter_entity:?} level",
        ));
    }

    Ok(cmp::min(user_entity, filter_entity))
}

pub fn parent_group_info_request_to_permission_groups(
    parent_groups: &[role_api::ParentGroupInfoRequest],
) -> Result<Vec<PermissionGroup>, UserErrors> {
    parent_groups
        .iter()
        .try_fold(Vec::new(), |mut permission_groups, parent_group| {
            let scopes = &parent_group.scopes;

            if scopes.is_empty() {
                return Err(UserErrors::InvalidRoleOperation);
            }

            let available_scopes = parent_group.name.get_available_scopes();

            if !scopes.iter().all(|scope| available_scopes.contains(scope)) {
                return Err(UserErrors::InvalidRoleOperation);
            }

            let groups = PermissionGroup::iter()
                .filter(|group| {
                    group.parent() == parent_group.name && scopes.contains(&group.scope())
                })
                .collect::<Vec<_>>();
            permission_groups.extend(groups);

            Ok(permission_groups)
        })
}

pub fn permission_groups_to_parent_group_info(
    permission_groups: &[PermissionGroup],
    entity_type: EntityType,
) -> Vec<role_api::ParentGroupInfo> {
    let parent_groups_map: HashMap<ParentGroup, Vec<common_enums::PermissionScope>> =
        permission_groups
            .iter()
            .fold(HashMap::new(), |mut acc, group| {
                let parent = group.parent();
                let scope = group.scope();
                acc.entry(parent).or_default().push(scope);
                acc
            });

    parent_groups_map
        .into_iter()
        .filter_map(|(name, scopes)| {
            let unique_scopes = scopes
                .into_iter()
                .collect::<HashSet<_>>()
                .into_iter()
                .collect();

            let description =
                ParentGroup::get_descriptions_for_groups(entity_type, permission_groups.to_vec())
                    .and_then(|descriptions| descriptions.get(&name).cloned())?;

            Some(role_api::ParentGroupInfo {
                name,
                description,
                scopes: unique_scopes,
            })
        })
        .collect()
}


pub struct MerchantConnectorAccount {
    pub merchant_id: id_type::MerchantId,
    pub connector_name: String,
    pub connector_account_details: Encryption,
    pub test_mode: Option<bool>,
    pub disabled: Option<bool>,
    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub payment_methods_enabled: Option<Vec<pii::SecretSerdeValue>>,
    pub connector_type: storage_enums::ConnectorType,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub connector_label: Option<String>,
    pub business_country: Option<storage_enums::CountryAlpha2>,
    pub business_label: Option<String>,
    pub business_sub_label: Option<String>,
    pub frm_configs: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub connector_webhook_details: Option<pii::SecretSerdeValue>,
    #[diesel(deserialize_as = super::OptionalDieselArray<pii::SecretSerdeValue>)]
    pub frm_config: Option<Vec<pii::SecretSerdeValue>>,
    pub profile_id: Option<id_type::ProfileId>,
    #[diesel(deserialize_as = super::OptionalDieselArray<String>)]
    pub applepay_verified_domains: Option<Vec<String>>,
    pub pm_auth_config: Option<pii::SecretSerdeValue>,
    pub status: storage_enums::ConnectorStatus,
    pub additional_merchant_data: Option<Encryption>,
    pub connector_wallets_details: Option<Encryption>,
    pub version: common_enums::ApiVersion,
    pub id: Option<id_type::MerchantConnectorAccountId>,
}


impl ConnectorValidation for Zsl {
    fn is_webhook_source_verification_mandatory(&self) -> bool {
        true
    }
}


pub struct MandateDetails {
    pub update_mandate_id: Option<String>,
}


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

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

                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let lambda_req = GenerateReportRequest {
                    request: payload,
                    merchant_id: Some(merchant_id.clone()),
                    auth: AuthInfo::MerchantLevel {
                        org_id: org_id.clone(),
                        merchant_ids: vec![merchant_id.clone()],
                    },
                    email: user_email,
                };

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


impl ConnectorValidation for Cashtocode {}

impl ConnectorValidation for for Cashtocode


pub fn disable_algorithm_id(&mut self, dynamic_routing_type: DynamicRoutingType) {
        match dynamic_routing_type {
            DynamicRoutingType::SuccessRateBasedRouting => {
                if let Some(success_based_algo) = &self.success_based_algorithm {
                    self.success_based_algorithm = Some(SuccessBasedAlgorithm {
                        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(None),
                        enabled_feature: success_based_algo.enabled_feature,
                    });
                }
            }
            DynamicRoutingType::EliminationRouting => {
                if let Some(elimination_based_algo) = &self.elimination_routing_algorithm {
                    self.elimination_routing_algorithm = Some(EliminationRoutingAlgorithm {
                        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(None),
                        enabled_feature: elimination_based_algo.enabled_feature,
                    });
                }
            }
            DynamicRoutingType::ContractBasedRouting => {
                if let Some(contract_based_algo) = &self.contract_based_routing {
                    self.contract_based_routing = Some(ContractRoutingAlgorithm {
                        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(None),
                        enabled_feature: contract_based_algo.enabled_feature,
                    });
                }
            }
        }
    }


impl ProxyRequestWrapper {
    pub async fn get_proxy_record(
        &self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: common_enums::enums::MerchantStorageScheme,
    ) -> RouterResult<ProxyRecord> {
        let token = &self.0.token;

        match self.0.token_type {
            proxy_api_models::TokenType::PaymentMethodId => {
                let pm_id = PaymentMethodId {
                    payment_method_id: token.clone(),
                };
                let pm_id =
                    id_type::GlobalPaymentMethodId::generate_from_string(pm_id.payment_method_id)
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Unable to generate GlobalPaymentMethodId")?;

                let payment_method_record = state
                    .store
                    .find_payment_method(&((state).into()), key_store, &pm_id, storage_scheme)
                    .await
                    .change_context(errors::ApiErrorResponse::PaymentMethodNotFound)?;
                Ok(ProxyRecord::PaymentMethodRecord(Box::new(
                    payment_method_record,
                )))
            }
            proxy_api_models::TokenType::TokenizationId => {
                let token_id = id_type::GlobalTokenId::from_string(token.clone().as_str())
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable(
                        "Error while coneverting from string to GlobalTokenId type",
                    )?;
                let db = state.store.as_ref();
                let key_manager_state = &(state).into();

                let tokenization_record = db
                    .get_entity_id_vault_id_by_token_id(&token_id, key_store, key_manager_state)
                    .await
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Error while fetching tokenization record from vault")?;

                Ok(ProxyRecord::TokenizationRecord(Box::new(
                    tokenization_record,
                )))
            }
        }
    }

    pub fn get_headers(&self) -> Vec<(String, masking::Maskable<String>)> {
        self.0
            .headers
            .as_map()
            .iter()
            .map(|(key, value)| (key.clone(), value.clone().into_masked()))
            .collect()
    }

    pub fn get_destination_url(&self) -> &str {
        self.0.destination_url.as_str()
    }

    pub fn get_method(&self) -> common_utils::request::Method {
        self.0.method
    }
}


pub struct PaymentIntentCount;


File: crates/router/src/routes/dummy_connector/errors.rs

#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ErrorType {
    ServerNotAvailable,
    ObjectNotFound,
    InvalidRequestError,
}

#[derive(Debug, Clone, router_derive::ApiError)]
#[error(error_type_enum = ErrorType)]
// TODO: Remove this line if InternalServerError is used anywhere
#[allow(dead_code)]
pub enum DummyConnectorErrors {
    #[error(error_type = ErrorType::ServerNotAvailable, code = "DC_00", message = "Something went wrong")]
    InternalServerError,

    #[error(error_type = ErrorType::ObjectNotFound, code = "DC_01", message = "Payment does not exist in our records")]
    PaymentNotFound,

    #[error(error_type = ErrorType::InvalidRequestError, code = "DC_02", message = "Missing required param: {field_name}")]
    MissingRequiredField { field_name: &'static str },

    #[error(error_type = ErrorType::InvalidRequestError, code = "DC_03", message = "The refund amount exceeds the amount captured")]
    RefundAmountExceedsPaymentAmount,

    #[error(error_type = ErrorType::InvalidRequestError, code = "DC_04", message = "Card not supported. Please use test cards")]
    CardNotSupported,

    #[error(error_type = ErrorType::ObjectNotFound, code = "DC_05", message = "Refund does not exist in our records")]
    RefundNotFound,

    #[error(error_type = ErrorType::InvalidRequestError, code = "DC_06", message = "Payment is not successful")]
    PaymentNotSuccessful,

    #[error(error_type = ErrorType::ServerNotAvailable, code = "DC_07", message = "Error occurred while storing the payment")]
    PaymentStoringError,

    #[error(error_type = ErrorType::InvalidRequestError, code = "DC_08", message = "Payment declined: {message}")]
    PaymentDeclined { message: &'static str },
}

impl core::fmt::Display for DummyConnectorErrors {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            r#"{{"error":{}}}"#,
            serde_json::to_string(self)
                .unwrap_or_else(|_| "Dummy connector error response".to_string())
        )
    }
}

impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorResponse>
    for DummyConnectorErrors
{
    fn switch(&self) -> api_models::errors::types::ApiErrorResponse {
        use api_models::errors::types::{ApiError, ApiErrorResponse as AER};
        match self {
            Self::InternalServerError => {
                AER::InternalServerError(ApiError::new("DC", 0, self.error_message(), None))
            }
            Self::PaymentNotFound => {
                AER::NotFound(ApiError::new("DC", 1, self.error_message(), None))
            }
            Self::MissingRequiredField { field_name: _ } => {
                AER::BadRequest(ApiError::new("DC", 2, self.error_message(), None))
            }
            Self::RefundAmountExceedsPaymentAmount => {
                AER::InternalServerError(ApiError::new("DC", 3, self.error_message(), None))
            }
            Self::CardNotSupported => {
                AER::BadRequest(ApiError::new("DC", 4, self.error_message(), None))
            }
            Self::RefundNotFound => {
                AER::NotFound(ApiError::new("DC", 5, self.error_message(), None))
            }
            Self::PaymentNotSuccessful => {
                AER::BadRequest(ApiError::new("DC", 6, self.error_message(), None))
            }
            Self::PaymentStoringError => {
                AER::InternalServerError(ApiError::new("DC", 7, self.error_message(), None))
            }
            Self::PaymentDeclined { message: _ } => {
                AER::BadRequest(ApiError::new("DC", 8, self.error_message(), None))
            }
        }
    }
}