File: crates/router/src/compatibility/stripe/setup_intents/types.rs
 Public structs: 11

use std::str::FromStr;

use api_models::payments;
use common_utils::{date_time, ext_traits::StringExt, id_type, pii as secret};
use error_stack::ResultExt;
use router_env::logger;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::{
    compatibility::stripe::{
        payment_intents::types as payment_intent, refunds::types as stripe_refunds,
    },
    consts,
    core::errors,
    pii::{self, PeekInterface},
    types::{
        api::{self as api_types, admin, enums as api_enums},
        transformers::{ForeignFrom, ForeignTryFrom},
    },
    utils::OptionExt,
};

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
pub struct StripeBillingDetails {
    pub address: Option<payments::AddressDetails>,
    pub email: Option<pii::Email>,
    pub name: Option<String>,
    pub phone: Option<pii::Secret<String>>,
}

impl From<StripeBillingDetails> for payments::Address {
    fn from(details: StripeBillingDetails) -> Self {
        Self {
            address: details.address,
            phone: Some(payments::PhoneDetails {
                number: details.phone,
                country_code: None,
            }),
            email: details.email,
        }
    }
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
pub struct StripeCard {
    pub number: cards::CardNumber,
    pub exp_month: pii::Secret<String>,
    pub exp_year: pii::Secret<String>,
    pub cvc: pii::Secret<String>,
}

// ApplePay wallet param is not available in stripe Docs
#[derive(Serialize, PartialEq, Eq, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripeWallet {
    ApplePay(payments::ApplePayWalletData),
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentMethodType {
    #[default]
    Card,
    Wallet,
}

impl From<StripePaymentMethodType> for api_enums::PaymentMethod {
    fn from(item: StripePaymentMethodType) -> Self {
        match item {
            StripePaymentMethodType::Card => Self::Card,
            StripePaymentMethodType::Wallet => Self::Wallet,
        }
    }
}
#[derive(Default, PartialEq, Eq, Deserialize, Clone)]
pub struct StripePaymentMethodData {
    #[serde(rename = "type")]
    pub stype: StripePaymentMethodType,
    pub billing_details: Option<StripeBillingDetails>,
    #[serde(flatten)]
    pub payment_method_details: Option<StripePaymentMethodDetails>, // enum
    pub metadata: Option<Value>,
}

#[derive(PartialEq, Eq, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentMethodDetails {
    Card(StripeCard),
    Wallet(StripeWallet),
}

impl From<StripeCard> for payments::Card {
    fn from(card: StripeCard) -> Self {
        Self {
            card_number: card.number,
            card_exp_month: card.exp_month,
            card_exp_year: card.exp_year,
            card_holder_name: Some(masking::Secret::new("stripe_cust".to_owned())),
            card_cvc: card.cvc,
            card_issuer: None,
            card_network: None,
            bank_code: None,
            card_issuing_country: None,
            card_type: None,
            nick_name: None,
        }
    }
}

impl From<StripeWallet> for payments::WalletData {
    fn from(wallet: StripeWallet) -> Self {
        match wallet {
            StripeWallet::ApplePay(data) => Self::ApplePay(data),
        }
    }
}

impl From<StripePaymentMethodDetails> for payments::PaymentMethodData {
    fn from(item: StripePaymentMethodDetails) -> Self {
        match item {
            StripePaymentMethodDetails::Card(card) => Self::Card(payments::Card::from(card)),
            StripePaymentMethodDetails::Wallet(wallet) => {
                Self::Wallet(payments::WalletData::from(wallet))
            }
        }
    }
}

#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
pub struct Shipping {
    pub address: Option<payments::AddressDetails>,
    pub name: Option<String>,
    pub carrier: Option<String>,
    pub phone: Option<pii::Secret<String>>,
    pub tracking_number: Option<pii::Secret<String>>,
}

impl From<Shipping> for payments::Address {
    fn from(details: Shipping) -> Self {
        Self {
            address: details.address,
            phone: Some(payments::PhoneDetails {
                number: details.phone,
                country_code: None,
            }),
            email: None,
        }
    }
}

#[derive(Default, Deserialize, Clone)]
pub struct StripeSetupIntentRequest {
    pub confirm: Option<bool>,
    pub customer: Option<id_type::CustomerId>,
    pub connector: Option<Vec<api_enums::RoutableConnectors>>,
    pub description: Option<String>,
    pub currency: Option<String>,
    pub payment_method_data: Option<StripePaymentMethodData>,
    pub receipt_email: Option<pii::Email>,
    pub return_url: Option<url::Url>,
    pub setup_future_usage: Option<api_enums::FutureUsage>,
    pub shipping: Option<Shipping>,
    pub billing_details: Option<StripeBillingDetails>,
    pub statement_descriptor: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    pub metadata: Option<secret::SecretSerdeValue>,
    pub client_secret: Option<pii::Secret<String>>,
    pub payment_method_options: Option<payment_intent::StripePaymentMethodOptions>,
    pub payment_method: Option<String>,
    pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
    pub receipt_ipaddress: Option<String>,
    pub user_agent: Option<String>,
    pub mandate_data: Option<payment_intent::MandateData>,
    pub connector_metadata: Option<payments::ConnectorMetadata>,
}

impl TryFrom<StripeSetupIntentRequest> for payments::PaymentsRequest {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn try_from(item: StripeSetupIntentRequest) -> errors::RouterResult<Self> {
        let routable_connector: Option<api_enums::RoutableConnectors> =
            item.connector.and_then(|v| v.into_iter().next());

        let routing = routable_connector
            .map(|connector| {
                api_models::routing::StaticRoutingAlgorithm::Single(Box::new(
                    api_models::routing::RoutableConnectorChoice {
                        choice_kind: api_models::routing::RoutableChoiceKind::FullStruct,
                        connector,
                        merchant_connector_id: None,
                    },
                ))
            })
            .map(|r| {
                serde_json::to_value(r)
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("converting to routing failed")
            })
            .transpose()?;
        let ip_address = item
            .receipt_ipaddress
            .map(|ip| std::net::IpAddr::from_str(ip.as_str()))
            .transpose()
            .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                field_name: "receipt_ipaddress".to_string(),
                expected_format: "127.0.0.1".to_string(),
            })?;
        let metadata_object = item
            .metadata
            .clone()
            .parse_value("metadata")
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: "metadata mapping failed",
            })?;
        let request = Ok(Self {
            amount: Some(api_types::Amount::Zero),
            capture_method: None,
            amount_to_capture: None,
            confirm: item.confirm,
            customer_id: item.customer,
            currency: item
                .currency
                .as_ref()
                .map(|c| c.to_uppercase().parse_enum("currency"))
                .transpose()
                .change_context(errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "currency",
                })?,
            email: item.receipt_email,
            name: item
                .billing_details
                .as_ref()
                .and_then(|b| b.name.as_ref().map(|x| masking::Secret::new(x.to_owned()))),
            phone: item.shipping.as_ref().and_then(|s| s.phone.clone()),
            description: item.description,
            return_url: item.return_url,
            payment_method_data: item.payment_method_data.as_ref().and_then(|pmd| {
                pmd.payment_method_details
                    .as_ref()
                    .map(|spmd| payments::PaymentMethodDataRequest {
                        payment_method_data: Some(payments::PaymentMethodData::from(
                            spmd.to_owned(),
                        )),
                        billing: pmd.billing_details.clone().map(payments::Address::from),
                    })
            }),
            payment_method: item
                .payment_method_data
                .as_ref()
                .map(|pmd| api_enums::PaymentMethod::from(pmd.stype.to_owned())),
            shipping: item
                .shipping
                .as_ref()
                .map(|s| payments::Address::from(s.to_owned())),
            billing: item
                .billing_details
                .as_ref()
                .map(|b| payments::Address::from(b.to_owned())),
            statement_descriptor_name: item.statement_descriptor,
            statement_descriptor_suffix: item.statement_descriptor_suffix,
            metadata: metadata_object,
            client_secret: item.client_secret.map(|s| s.peek().clone()),
            setup_future_usage: item.setup_future_usage,
            merchant_connector_details: item.merchant_connector_details,
            routing,
            authentication_type: match item.payment_method_options {
                Some(pmo) => {
                    let payment_intent::StripePaymentMethodOptions::Card {
                        request_three_d_secure,
                    }: payment_intent::StripePaymentMethodOptions = pmo;
                    Some(api_enums::AuthenticationType::foreign_from(
                        request_three_d_secure,
                    ))
                }
                None => None,
            },
            mandate_data: ForeignTryFrom::foreign_try_from((
                item.mandate_data,
                item.currency.to_owned(),
            ))?,
            browser_info: Some(
                serde_json::to_value(crate::types::BrowserInformation {
                    ip_address,
                    user_agent: item.user_agent,
                    ..Default::default()
                })
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("convert to browser info failed")?,
            ),
            connector_metadata: item.connector_metadata,
            ..Default::default()
        });
        request
    }
}

#[derive(Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StripeSetupStatus {
    Succeeded,
    Canceled,
    #[default]
    Processing,
    RequiresAction,
    RequiresPaymentMethod,
    RequiresConfirmation,
}

impl From<api_enums::IntentStatus> for StripeSetupStatus {
    fn from(item: api_enums::IntentStatus) -> Self {
        match item {
            api_enums::IntentStatus::Succeeded | api_enums::IntentStatus::PartiallyCaptured => {
                Self::Succeeded
            }
            api_enums::IntentStatus::Failed | api_enums::IntentStatus::Expired => Self::Canceled,
            api_enums::IntentStatus::Processing
            | api_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture => Self::Processing,
            api_enums::IntentStatus::RequiresCustomerAction => Self::RequiresAction,
            api_enums::IntentStatus::RequiresMerchantAction
            | api_enums::IntentStatus::Conflicted => Self::RequiresAction,
            api_enums::IntentStatus::RequiresPaymentMethod => Self::RequiresPaymentMethod,
            api_enums::IntentStatus::RequiresConfirmation => Self::RequiresConfirmation,
            api_enums::IntentStatus::RequiresCapture
            | api_enums::IntentStatus::PartiallyCapturedAndCapturable => {
                logger::error!("Invalid status change");
                Self::Canceled
            }
            api_enums::IntentStatus::Cancelled | api_enums::IntentStatus::CancelledPostCapture => {
                Self::Canceled
            }
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Copy, Clone, strum::Display)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CancellationReason {
    Duplicate,
    Fraudulent,
    RequestedByCustomer,
    Abandoned,
}

#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
pub struct StripePaymentCancelRequest {
    cancellation_reason: Option<CancellationReason>,
}

impl From<StripePaymentCancelRequest> for payments::PaymentsCancelRequest {
    fn from(item: StripePaymentCancelRequest) -> Self {
        Self {
            cancellation_reason: item.cancellation_reason.map(|c| c.to_string()),
            ..Self::default()
        }
    }
}
#[derive(Default, Eq, PartialEq, Serialize)]
pub struct RedirectUrl {
    pub return_url: Option<String>,
    pub url: Option<String>,
}

#[derive(Eq, PartialEq, serde::Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum StripeNextAction {
    RedirectToUrl {
        redirect_to_url: RedirectUrl,
    },
    DisplayBankTransferInformation {
        bank_transfer_steps_and_charges_details: payments::BankTransferNextStepsData,
    },
    ThirdPartySdkSessionToken {
        session_token: Option<payments::SessionToken>,
    },
    QrCodeInformation {
        image_data_url: Option<url::Url>,
        display_to_timestamp: Option<i64>,
        qr_code_url: Option<url::Url>,
        border_color: Option<String>,
        display_text: Option<String>,
    },
    FetchQrCodeInformation {
        qr_code_fetch_url: url::Url,
    },
    DisplayVoucherInformation {
        voucher_details: payments::VoucherNextStepData,
    },
    WaitScreenInformation {
        display_from_timestamp: i128,
        display_to_timestamp: Option<i128>,
        poll_config: Option<payments::PollConfig>,
    },
    InvokeSdkClient {
        next_action_data: payments::SdkNextActionData,
    },
    CollectOtp {
        consent_data_required: payments::MobilePaymentConsent,
    },
    InvokeHiddenIframe {
        iframe_data: payments::IframeData,
    },
}

pub(crate) fn into_stripe_next_action(
    next_action: Option<payments::NextActionData>,
    return_url: Option<String>,
) -> Option<StripeNextAction> {
    next_action.map(|next_action_data| match next_action_data {
        payments::NextActionData::RedirectToUrl { redirect_to_url } => {
            StripeNextAction::RedirectToUrl {
                redirect_to_url: RedirectUrl {
                    return_url,
                    url: Some(redirect_to_url),
                },
            }
        }
        payments::NextActionData::RedirectInsidePopup { popup_url, .. } => {
            StripeNextAction::RedirectToUrl {
                redirect_to_url: RedirectUrl {
                    return_url,
                    url: Some(popup_url),
                },
            }
        }
        payments::NextActionData::DisplayBankTransferInformation {
            bank_transfer_steps_and_charges_details,
        } => StripeNextAction::DisplayBankTransferInformation {
            bank_transfer_steps_and_charges_details,
        },
        payments::NextActionData::ThirdPartySdkSessionToken { session_token } => {
            StripeNextAction::ThirdPartySdkSessionToken { session_token }
        }

        payments::NextActionData::QrCodeInformation {
            image_data_url,
            display_to_timestamp,
            qr_code_url,
            display_text,
            border_color,
        } => StripeNextAction::QrCodeInformation {
            image_data_url,
            display_to_timestamp,
            qr_code_url,
            display_text,
            border_color,
        },
        payments::NextActionData::FetchQrCodeInformation { qr_code_fetch_url } => {
            StripeNextAction::FetchQrCodeInformation { qr_code_fetch_url }
        }
        payments::NextActionData::DisplayVoucherInformation { voucher_details } => {
            StripeNextAction::DisplayVoucherInformation { voucher_details }
        }
        payments::NextActionData::WaitScreenInformation {
            display_from_timestamp,
            display_to_timestamp,
            poll_config: _,
        } => StripeNextAction::WaitScreenInformation {
            display_from_timestamp,
            display_to_timestamp,
            poll_config: None,
        },
        payments::NextActionData::ThreeDsInvoke { .. } => StripeNextAction::RedirectToUrl {
            redirect_to_url: RedirectUrl {
                return_url: None,
                url: None,
            },
        },
        payments::NextActionData::InvokeSdkClient { next_action_data } => {
            StripeNextAction::InvokeSdkClient { next_action_data }
        }
        payments::NextActionData::CollectOtp {
            consent_data_required,
        } => StripeNextAction::CollectOtp {
            consent_data_required,
        },
        payments::NextActionData::InvokeHiddenIframe { iframe_data } => {
            StripeNextAction::InvokeHiddenIframe { iframe_data }
        }
    })
}

#[derive(Default, Eq, PartialEq, Serialize)]
pub struct StripeSetupIntentResponse {
    pub id: id_type::PaymentId,
    pub object: String,
    pub status: StripeSetupStatus,
    pub client_secret: Option<masking::Secret<String>>,
    pub metadata: Option<Value>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub created: Option<time::PrimitiveDateTime>,
    pub customer: Option<id_type::CustomerId>,
    pub refunds: Option<Vec<stripe_refunds::StripeRefundResponse>>,
    pub mandate_id: Option<String>,
    pub next_action: Option<StripeNextAction>,
    pub last_payment_error: Option<LastPaymentError>,
    pub charges: payment_intent::Charges,
    pub connector_transaction_id: Option<String>,
}

#[derive(Default, Eq, PartialEq, Serialize)]
pub struct LastPaymentError {
    charge: Option<String>,
    code: Option<String>,
    decline_code: Option<String>,
    message: String,
    param: Option<String>,
    payment_method: StripePaymentMethod,
    #[serde(rename = "type")]
    error_type: String,
}

#[derive(Default, Eq, PartialEq, Serialize)]
pub struct StripePaymentMethod {
    #[serde(rename = "id")]
    payment_method_id: String,
    object: &'static str,
    card: Option<StripeCard>,
    created: u64,
    #[serde(rename = "type")]
    method_type: String,
    livemode: bool,
}

impl From<payments::PaymentsResponse> for StripeSetupIntentResponse {
    fn from(resp: payments::PaymentsResponse) -> Self {
        Self {
            object: "setup_intent".to_owned(),
            status: StripeSetupStatus::from(resp.status),
            client_secret: resp.client_secret,
            charges: payment_intent::Charges::new(),
            created: resp.created,
            customer: resp.customer_id,
            metadata: resp.metadata,
            id: resp.payment_id,
            refunds: resp
                .refunds
                .map(|a| a.into_iter().map(Into::into).collect()),
            mandate_id: resp.mandate_id,
            next_action: into_stripe_next_action(resp.next_action, resp.return_url),
            last_payment_error: resp.error_code.map(|code| -> LastPaymentError {
                LastPaymentError {
                    charge: None,
                    code: Some(code.to_owned()),
                    decline_code: None,
                    message: resp
                        .error_message
                        .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
                    param: None,
                    payment_method: StripePaymentMethod {
                        payment_method_id: "place_holder_id".to_string(),
                        object: "payment_method",
                        card: None,
                        created: u64::try_from(date_time::now().assume_utc().unix_timestamp())
                            .unwrap_or_default(),
                        method_type: "card".to_string(),
                        livemode: false,
                    },
                    error_type: code,
                }
            }),
            connector_transaction_id: resp.connector_transaction_id,
        }
    }
}
#[derive(Clone, Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct StripePaymentListConstraints {
    pub customer: Option<id_type::CustomerId>,
    pub starting_after: Option<id_type::PaymentId>,
    pub ending_before: Option<id_type::PaymentId>,
    #[serde(default = "default_limit")]
    pub limit: u32,
    pub created: Option<i64>,
    #[serde(rename = "created[lt]")]
    pub created_lt: Option<i64>,
    #[serde(rename = "created[gt]")]
    pub created_gt: Option<i64>,
    #[serde(rename = "created[lte]")]
    pub created_lte: Option<i64>,
    #[serde(rename = "created[gte]")]
    pub created_gte: Option<i64>,
}

fn default_limit() -> u32 {
    10
}

impl TryFrom<StripePaymentListConstraints> for payments::PaymentListConstraints {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn try_from(item: StripePaymentListConstraints) -> Result<Self, Self::Error> {
        Ok(Self {
            customer_id: item.customer,
            starting_after: item.starting_after,
            ending_before: item.ending_before,
            limit: item.limit,
            created: from_timestamp_to_datetime(item.created)?,
            created_lt: from_timestamp_to_datetime(item.created_lt)?,
            created_gt: from_timestamp_to_datetime(item.created_gt)?,
            created_lte: from_timestamp_to_datetime(item.created_lte)?,
            created_gte: from_timestamp_to_datetime(item.created_gte)?,
        })
    }
}

#[inline]
fn from_timestamp_to_datetime(
    time: Option<i64>,
) -> Result<Option<time::PrimitiveDateTime>, errors::ApiErrorResponse> {
    if let Some(time) = time {
        let time = time::OffsetDateTime::from_unix_timestamp(time).map_err(|err| {
            logger::error!("Error: from_unix_timestamp: {}", err);
            errors::ApiErrorResponse::InvalidRequestData {
                message: "Error while converting timestamp".to_string(),
            }
        })?;

        Ok(Some(time::PrimitiveDateTime::new(time.date(), time.time())))
    } else {
        Ok(None)
    }
}


impl api::PaymentToken for Santander {}

impl api::PaymentToken for for Santander


pub struct ExternalVaultConnectorMetadata {
    pub proxy_url: common_utils::types::Url,
    pub certificate: Secret<String>,
}


pub async fn find_metadata_by_algorithm_id_profile_id(
        conn: &PgPooledConn,
        algorithm_id: &common_utils::id_type::RoutingId,
        profile_id: &common_utils::id_type::ProfileId,
    ) -> StorageResult<RoutingProfileMetadata> {
        Self::table()
            .select((
                dsl::profile_id,
                dsl::algorithm_id,
                dsl::name,
                dsl::description,
                dsl::kind,
                dsl::created_at,
                dsl::modified_at,
                dsl::algorithm_for,
            ))
            .filter(
                dsl::algorithm_id
                    .eq(algorithm_id.to_owned())
                    .and(dsl::profile_id.eq(profile_id.to_owned())),
            )
            .limit(1)
            .load_async::<(
                common_utils::id_type::ProfileId,
                common_utils::id_type::RoutingId,
                String,
                Option<String>,
                enums::RoutingAlgorithmKind,
                PrimitiveDateTime,
                PrimitiveDateTime,
                enums::TransactionType,
            )>(conn)
            .await
            .change_context(DatabaseError::Others)?
            .into_iter()
            .next()
            .ok_or(report!(DatabaseError::NotFound))
            .map(
                |(
                    profile_id,
                    algorithm_id,
                    name,
                    description,
                    kind,
                    created_at,
                    modified_at,
                    algorithm_for,
                )| {
                    RoutingProfileMetadata {
                        profile_id,
                        algorithm_id,
                        name,
                        description,
                        kind,
                        created_at,
                        modified_at,
                        algorithm_for,
                    }
                },
            )
    }


pub struct GetInfoResponse {
    pub metrics: Vec<NameDescription>,
    pub download_dimensions: Option<Vec<NameDescription>>,
    pub dimensions: Vec<NameDescription>,
}


pub struct MonerisCard {
    card_number: cards::CardNumber,
    expiry_month: Secret<i64>,
    expiry_year: Secret<i64>,
    card_security_code: Secret<String>,
}


impl api::Payment for Prophetpay {}


impl Responder {
    let flow = Flow::WebhookEventDeliveryAttemptList;
    let (merchant_id, initial_attempt_id) = path.into_inner();

    let request_internal = WebhookDeliveryAttemptListRequestInternal {
        merchant_id: merchant_id.clone(),
        initial_attempt_id,
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        request_internal,
        |state, _, request_internal, _| {
            webhook_events::list_delivery_attempts(
                state,
                request_internal.merchant_id,
                request_internal.initial_attempt_id,
            )
        },
        auth::auth_type(
            &auth::AdminApiAuth,
            &auth::JWTAuthMerchantFromRoute {
                merchant_id,
                required_permission: Permission::MerchantWebhookEventRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


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

use masking::Secret;
use router::types::{self, api, domain, storage::enums};
use test_utils::connector_auth;

use crate::utils::{self, ConnectorActions};

#[derive(Clone, Copy)]
struct ItaubankTest;
impl ConnectorActions for ItaubankTest {}
impl utils::Connector for ItaubankTest {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Itaubank;
        utils::construct_connector_data_old(
            Box::new(Itaubank::new()),
            types::Connector::Itaubank,
            api::GetToken::Connector,
            None,
        )
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .itaubank
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }

    fn get_name(&self) -> String {
        "itaubank".to_string()
    }
}

static CONNECTOR: ItaubankTest = ItaubankTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests


pub fn get_routing_algorithm_id<'a>(
        &'a self,
        transaction_data: &'a routing::TransactionData<'_>,
    ) -> Option<id_type::RoutingId> {
        match transaction_data {
            routing::TransactionData::Payment(_) => self.profile.routing_algorithm_id.clone(),
            #[cfg(feature = "payouts")]
            routing::TransactionData::Payout(_) => self.profile.payout_routing_algorithm_id.clone(),
        }
    }


pub struct Purchase {
    #[serde(with = "common_utils::custom_serde::iso8601")]
    created_at: PrimitiveDateTime,
    order_channel: OrderChannel,
    total_price: i64,
    products: Vec<Products>,
    shipments: Shipments,
    currency: Option<Currency>,
    total_shipping_cost: Option<i64>,
    confirmation_email: Option<Email>,
    confirmation_phone: Option<Secret<String>>,
}


pub struct BraintreeThreeDsErrorResponse {
    pub code: String,
    pub message: String,
}


impl api::Refund for Bamboraapac {}


File: crates/analytics/src/outgoing_webhook_event.rs

mod core;
pub mod events;

pub trait OutgoingWebhookEventAnalytics: events::OutgoingWebhookLogsFilterAnalytics {}

pub use self::core::outgoing_webhook_events_core;


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

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        None
    }

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

impl ConnectorSpecifications for for Payone


pub struct RoutingActivatePayload {
    pub transaction_type: Option<TransactionType>,
}


pub struct OperationProperties {
    operations: Vec<Conversion>,
    flows: Vec<Derives>,
}


impl std::ops::Deref for RedisValue {
    type Target = FredRedisValue;

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


pub fn new(
        status: DummyConnectorStatus,
        id: String,
        currency: Currency,
        created: PrimitiveDateTime,
        payment_amount: i64,
        refund_amount: i64,
    ) -> Self {
        Self {
            status,
            id,
            currency,
            created,
            payment_amount,
            refund_amount,
        }
    }


pub fn server(config: AppState) -> Scope {
        web::scope("/poll")
            .app_data(web::Data::new(config))
            .service(
                web::resource("/status/{poll_id}").route(web::get().to(poll::retrieve_poll_status)),
            )
    }


pub struct DisputeNew {
    pub dispute_id: String,
    pub amount: StringMinorUnit,
    pub currency: String,
    pub dispute_stage: storage_enums::DisputeStage,
    pub dispute_status: storage_enums::DisputeStatus,
    pub payment_id: common_utils::id_type::PaymentId,
    pub attempt_id: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub connector_status: String,
    pub connector_dispute_id: String,
    pub connector_reason: Option<String>,
    pub connector_reason_code: Option<String>,
    pub challenge_required_by: Option<PrimitiveDateTime>,
    pub connector_created_at: Option<PrimitiveDateTime>,
    pub connector_updated_at: Option<PrimitiveDateTime>,
    pub connector: String,
    pub evidence: Option<Secret<serde_json::Value>>,
    pub profile_id: Option<common_utils::id_type::ProfileId>,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub dispute_amount: MinorUnit,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub dispute_currency: Option<storage_enums::Currency>,
}


pub struct RefundData {
    amount: u64,
    currency: common_enums::Currency,
    payment_type: Option<String>,
    tid: Option<Secret<i64>>,
}


pub struct ValuedRule<O> {
    pub name: String,
    pub connector_selection: O,
    pub statements: Vec<ValuedIfStatement>,
}


impl api::PaymentToken for Worldpayxml {}

impl api::PaymentToken for for Worldpayxml


File: crates/hyperswitch_domain_models/src/merchant_context.rs
 Public functions: 2
 Public structs: 1

pub use crate::{merchant_account::MerchantAccount, merchant_key_store::MerchantKeyStore};

/// `MerchantContext` represents the authentication and operational context for a merchant.
///
/// This enum encapsulates the merchant's account information and cryptographic keys
/// needed for secure operations. Currently supports only normal merchant operations,
/// but the enum structure allows for future expansion to different merchant types for example a
/// **platform** context.
#[derive(Clone, Debug)]
pub enum MerchantContext {
    /// Represents a normal operation merchant context.
    NormalMerchant(Box<Context>),
}

/// `Context` holds the merchant account details and cryptographic key store.
#[derive(Clone, Debug)]
pub struct Context(pub MerchantAccount, pub MerchantKeyStore);

impl MerchantContext {
    pub fn get_merchant_account(&self) -> &MerchantAccount {
        match self {
            Self::NormalMerchant(merchant_account) => &merchant_account.0,
        }
    }

    pub fn get_merchant_key_store(&self) -> &MerchantKeyStore {
        match self {
            Self::NormalMerchant(merchant_account) => &merchant_account.1,
        }
    }
}


OpenAPI Block Path: paths."/payouts/{payout_id}/fulfill"
{
  "post": {
    "tags": [
      "Payouts"
    ],
    "summary": "Payouts - Fulfill",
    "operationId": "Fulfill a Payout",
    "parameters": [
      {
        "name": "payout_id",
        "in": "path",
        "description": "The identifier for payout",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    ],
    "requestBody": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/PayoutFulfillRequest"
          }
        }
      },
      "required": true
    },
    "responses": {
      "200": {
        "description": "Payout fulfilled",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PayoutCreateResponse"
            }
          }
        }
      },
      "400": {
        "description": "Missing Mandatory fields"
      }
    },
    "security": [
      {
        "api_key": []
      }
    ]
  }
}


pub struct TokenizedCardValue2 {
    pub card_security_code: Option<String>,
    pub card_fingerprint: Option<String>,
    pub external_id: Option<String>,
    pub customer_id: Option<id_type::CustomerId>,
    pub payment_method_id: Option<String>,
}


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

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

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

impl ConnectorSpecifications for for Trustpay


impl ApiEventMetric for AuthenticationResponse {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Authentication {
            authentication_id: self.authentication_id.clone(),
        })
    }
}

impl ApiEventMetric for for AuthenticationResponse


File: crates/router/src/cors.rs
 Public functions: 1

// use actix_web::http::header;

use crate::configs::settings;

pub fn cors(config: settings::CorsSettings) -> actix_cors::Cors {
    let allowed_methods = config.allowed_methods.iter().map(|s| s.as_str());

    let mut cors = actix_cors::Cors::default()
        .allowed_methods(allowed_methods)
        .allow_any_header()
        .expose_any_header()
        .max_age(config.max_age);

    if config.wildcard_origin {
        cors = cors.allow_any_origin()
    } else {
        for origin in &config.origins {
            cors = cors.allowed_origin(origin);
        }
        // Only allow this in case if it's not wildcard origins. ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
        cors = cors.supports_credentials();
    }

    cors
}


pub struct BarclaycardVoidRequest {
    client_reference_information: ClientReferenceInformation,
    reversal_information: ReversalInformation,
    #[serde(skip_serializing_if = "Option::is_none")]
    merchant_defined_information: Option<Vec<MerchantDefinedInformation>>,
    // The connector documentation does not mention the merchantDefinedInformation field for Void requests. But this has been still added because it works!
}


    request_return_url: Option<&String>,
    client_secret: Option<&masking::Secret<String>>,
    manual_retry_allowed: Option<bool>,
) -> RouterResult<String> {
    // take return url if provided in the request else use merchant return url
    let url = request_return_url
        .or(business_profile.return_url.as_ref())
        .get_required_value("return_url")?;

    let status_check = redirection_response.status;

    let payment_client_secret = client_secret
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Expected client secret to be `Some`")?;

    let payment_id = redirection_response.payment_id.get_string_repr().to_owned();
    let merchant_url_with_response = if business_profile.redirect_to_merchant_with_http_post {
        url::Url::parse_with_params(
            url,
            &[
                ("status", status_check.to_string()),
                ("payment_id", payment_id),
                (
                    "payment_intent_client_secret",
                    payment_client_secret.peek().to_string(),
                ),
                (
                    "manual_retry_allowed",
                    manual_retry_allowed.unwrap_or(false).to_string(),
                ),
            ],
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to parse the url with param")?
    } else {
        let amount = redirection_response.amount.get_required_value("amount")?;
        url::Url::parse_with_params(
            url,
            &[
                ("status", status_check.to_string()),
                ("payment_id", payment_id),
                (
                    "payment_intent_client_secret",
                    payment_client_secret.peek().to_string(),
                ),
                ("amount", amount.to_string()),
                (
                    "manual_retry_allowed",
                    manual_retry_allowed.unwrap_or(false).to_string(),
                ),
            ],
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to parse the url with param")?
    };

    Ok(merchant_url_with_response.to_string())
}

#[cfg(feature = "v1")]
pub async fn make_ephemeral_key(
    state: SessionState,
    customer_id: id_type::CustomerId,
    merchant_id: id_type::MerchantId,
) -> errors::RouterResponse<ephemeral_key::EphemeralKey> {
    let store = &state.store;
    let id = utils::generate_id(consts::ID_LENGTH, "eki");
    let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string());
    let ek = ephemeral_key::EphemeralKeyNew {
        id,
        customer_id,
        merchant_id: merchant_id.to_owned(),
        secret,
    };
    let ek = store
        .create_ephemeral_key(ek, state.conf.eph_key.validity)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to create ephemeral key")?;
    Ok(services::ApplicationResponse::Json(ek))
}

#[cfg(feature = "v2")]
pub async fn make_client_secret(
    state: SessionState,
    resource_id: api_models::ephemeral_key::ResourceId,
    merchant_context: domain::MerchantContext,
    headers: &actix_web::http::header::HeaderMap,
) -> errors::RouterResponse<ClientSecretResponse> {
    let db = &state.store;
    let key_manager_state = &((&state).into());

    match &resource_id {
        api_models::ephemeral_key::ResourceId::Customer(global_customer_id) => {
            db.find_customer_by_global_id(
                key_manager_state,
                global_customer_id,
                merchant_context.get_merchant_key_store(),
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;
        }
    }

    let resource_id = match resource_id {
        api_models::ephemeral_key::ResourceId::Customer(global_customer_id) => {
            common_utils::types::authentication::ResourceId::Customer(global_customer_id)
        }
    };

    let client_secret = create_client_secret(
        &state,
        merchant_context.get_merchant_account().get_id(),
        resource_id,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Unable to create client secret")?;

    let response = ClientSecretResponse::foreign_try_from(client_secret)
        .attach_printable("Only customer is supported as resource_id in response")?;
    Ok(services::ApplicationResponse::Json(response))
}

#[cfg(feature = "v2")]
pub async fn create_client_secret(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    resource_id: common_utils::types::authentication::ResourceId,
) -> RouterResult<ephemeral_key::ClientSecretType> {
    use common_utils::generate_time_ordered_id;

    let store = &state.store;
    let id = id_type::ClientSecretId::generate();
    let secret = masking::Secret::new(generate_time_ordered_id("cs"));

    let client_secret = ephemeral_key::ClientSecretTypeNew {
        id,
        merchant_id: merchant_id.to_owned(),
        secret,
        resource_id,
    };
    let client_secret = store
        .create_client_secret(client_secret, state.conf.eph_key.validity)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to create client secret")?;
    Ok(client_secret)
}

#[cfg(feature = "v1")]
pub async fn delete_ephemeral_key(
    state: SessionState,
    ek_id: String,
) -> errors::RouterResponse<ephemeral_key::EphemeralKey> {
    let db = state.store.as_ref();
    let ek = db
        .delete_ephemeral_key(&ek_id)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to delete ephemeral key")?;
    Ok(services::ApplicationResponse::Json(ek))
}

#[cfg(feature = "v2")]
pub async fn delete_client_secret(
    state: SessionState,
    ephemeral_key_id: String,
) -> errors::RouterResponse<ClientSecretResponse> {
    let db = state.store.as_ref();
    let ephemeral_key = db
        .delete_client_secret(&ephemeral_key_id)
        .await
        .map_err(|err| match err.current_context() {
            errors::StorageError::ValueNotFound(_) => {
                err.change_context(errors::ApiErrorResponse::GenericNotFoundError {
                    message: "Ephemeral Key not found".to_string(),
                })
            }
            _ => err.change_context(errors::ApiErrorResponse::InternalServerError),
        })
        .attach_printable("Unable to delete ephemeral key")?;

    let response = ClientSecretResponse::foreign_try_from(ephemeral_key)
        .attach_printable("Only customer is supported as resource_id in response")?;
    Ok(services::ApplicationResponse::Json(response))
}

#[cfg(feature = "v1")]
pub fn make_pg_redirect_response(
    payment_id: id_type::PaymentId,
    response: &api::PaymentsResponse,
    connector: String,
) -> api::PgRedirectResponse {
    api::PgRedirectResponse {
        payment_id,
        status: response.status,
        gateway_id: connector,
        customer_id: response.customer_id.to_owned(),
        amount: Some(response.amount),
    }
}

#[cfg(feature = "v1")]
pub fn make_pg_redirect_response_for_authentication(
    authentication_id: id_type::AuthenticationId,
    connector: String,
    amount: Option<MinorUnit>,
    trans_status: common_enums::TransactionStatus,
) -> hyperswitch_domain_models::authentication::PgRedirectResponseForAuthentication {
    hyperswitch_domain_models::authentication::PgRedirectResponseForAuthentication {
        authentication_id,
        status: trans_status,
        gateway_id: connector,
        customer_id: None,
        amount,
    }
}

#[cfg(feature = "v1")]
pub fn make_url_with_signature(
    redirect_url: &str,
    business_profile: &domain::Profile,
) -> RouterResult<api::RedirectionResponse> {
    let mut url = url::Url::parse(redirect_url)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to parse the url")?;

    let mut base_url = url.clone();
    base_url.query_pairs_mut().clear();

    let url = if business_profile.enable_payment_response_hash {
        let key = business_profile
            .payment_response_hash_key
            .as_ref()
            .get_required_value("payment_response_hash_key")?;
        let signature = hmac_sha512_sorted_query_params(
            &mut url.query_pairs().collect::<Vec<_>>(),
            key.as_str(),
        )?;

        url.query_pairs_mut()
            .append_pair("signature", &signature)
            .append_pair("signature_algorithm", "HMAC-SHA512");
        url.to_owned()
    } else {
        url.to_owned()
    };

    let parameters = url
        .query_pairs()
        .collect::<Vec<_>>()
        .iter()
        .map(|(key, value)| (key.clone().into_owned(), value.clone().into_owned()))
        .collect::<Vec<_>>();

    Ok(api::RedirectionResponse {
        return_url: base_url.to_string(),
        params: parameters,
        return_url_with_query_params: url.to_string(),
        http_method: if business_profile.redirect_to_merchant_with_http_post {
            services::Method::Post.to_string()
        } else {
            services::Method::Get.to_string()
        },
        headers: Vec::new(),
    })
}

pub fn hmac_sha512_sorted_query_params(
    params: &mut [(Cow<'_, str>, Cow<'_, str>)],
    key: &str,
) -> RouterResult<String> {
    params.sort();
    let final_string = params
        .iter()
        .map(|(key, value)| format!("{key}={value}"))
        .collect::<Vec<_>>()
        .join("&");

    let signature = crypto::HmacSha512::sign_message(
        &crypto::HmacSha512,
        key.as_bytes(),
        final_string.as_bytes(),
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to sign the message")?;

    Ok(hex::encode(signature))
}

pub fn check_if_operation_confirm<Op: std::fmt::Debug>(operations: Op) -> bool {
    format!("{operations:?}") == "PaymentConfirm"
}

#[allow(clippy::too_many_arguments)]
pub fn generate_mandate(
    merchant_id: id_type::MerchantId,
    payment_id: id_type::PaymentId,
    connector: String,
    setup_mandate_details: Option<MandateData>,
    customer_id: &Option<id_type::CustomerId>,
    payment_method_id: String,
    connector_mandate_id: Option<pii::SecretSerdeValue>,
    network_txn_id: Option<String>,
    payment_method_data_option: Option<domain::payments::PaymentMethodData>,
    mandate_reference: Option<MandateReference>,
    merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
) -> CustomResult<Option<storage::MandateNew>, errors::ApiErrorResponse> {
    match (setup_mandate_details, customer_id) {
        (Some(data), Some(cus_id)) => {
            let mandate_id = utils::generate_id(consts::ID_LENGTH, "man");

            // The construction of the mandate new must be visible
            let mut new_mandate = storage::MandateNew::default();

            let customer_acceptance = data
                .customer_acceptance
                .get_required_value("customer_acceptance")?;
            new_mandate
                .set_mandate_id(mandate_id)
                .set_customer_id(cus_id.clone())
                .set_merchant_id(merchant_id)
                .set_original_payment_id(Some(payment_id))
                .set_payment_method_id(payment_method_id)
                .set_connector(connector)
                .set_mandate_status(storage_enums::MandateStatus::Active)
                .set_connector_mandate_ids(connector_mandate_id)
                .set_network_transaction_id(network_txn_id)
                .set_customer_ip_address(
                    customer_acceptance
                        .get_ip_address()
                        .map(masking::Secret::new),
                )
                .set_customer_user_agent_extended(customer_acceptance.get_user_agent())
                .set_customer_accepted_at(Some(customer_acceptance.get_accepted_at()))
                .set_metadata(payment_method_data_option.map(|payment_method_data| {
                    pii::SecretSerdeValue::new(
                        serde_json::to_value(payment_method_data).unwrap_or_default(),
                    )
                }))
                .set_connector_mandate_id(
                    mandate_reference.and_then(|reference| reference.connector_mandate_id),
                )
                .set_merchant_connector_id(merchant_connector_id);

            Ok(Some(
                match data.mandate_type.get_required_value("mandate_type")? {
                    hyperswitch_domain_models::mandates::MandateDataType::SingleUse(data) => {
                        new_mandate
                            .set_mandate_amount(Some(data.amount.get_amount_as_i64()))
                            .set_mandate_currency(Some(data.currency))
                            .set_mandate_type(storage_enums::MandateType::SingleUse)
                            .to_owned()
                    }

                    hyperswitch_domain_models::mandates::MandateDataType::MultiUse(op_data) => {
                        match op_data {
                            Some(data) => new_mandate
                                .set_mandate_amount(Some(data.amount.get_amount_as_i64()))
                                .set_mandate_currency(Some(data.currency))
                                .set_start_date(data.start_date)
                                .set_end_date(data.end_date),
                            // .set_metadata(data.metadata),
                            // we are storing PaymentMethodData in metadata of mandate
                            None => &mut new_mandate,
                        }
                        .set_mandate_type(storage_enums::MandateType::MultiUse)
                        .to_owned()
                    }
                },
            ))
        }
        (_, _) => Ok(None),
    }
}

#[cfg(feature = "v1")]
// A function to manually authenticate the client secret with intent fulfillment time
pub fn authenticate_client_secret(
    request_client_secret: Option<&String>,
    payment_intent: &PaymentIntent,
) -> Result<(), errors::ApiErrorResponse> {
    match (request_client_secret, &payment_intent.client_secret) {
        (Some(req_cs), Some(pi_cs)) => {
            if req_cs != pi_cs {
                Err(errors::ApiErrorResponse::ClientSecretInvalid)
            } else {
                let current_timestamp = common_utils::date_time::now();

                let session_expiry = payment_intent.session_expiry.unwrap_or(
                    payment_intent
                        .created_at
                        .saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)),
                );

                fp_utils::when(current_timestamp > session_expiry, || {
                    Err(errors::ApiErrorResponse::ClientSecretExpired)
                })
            }
        }
        // If there is no client in payment intent, then it has expired
        (Some(_), None) => Err(errors::ApiErrorResponse::ClientSecretExpired),
        _ => Ok(()),
    }
}

pub(crate) fn validate_payment_status_against_allowed_statuses(
    intent_status: storage_enums::IntentStatus,
    allowed_statuses: &[storage_enums::IntentStatus],
    action: &'static str,
) -> Result<(), errors::ApiErrorResponse> {
    fp_utils::when(!allowed_statuses.contains(&intent_status), || {
        Err(errors::ApiErrorResponse::PreconditionFailed {
            message: format!(
                "You cannot {action} this payment because it has status {intent_status}",
            ),
        })
    })
}

pub(crate) fn validate_payment_status_against_not_allowed_statuses(
    intent_status: storage_enums::IntentStatus,
    not_allowed_statuses: &[storage_enums::IntentStatus],
    action: &'static str,
) -> Result<(), errors::ApiErrorResponse> {
    fp_utils::when(not_allowed_statuses.contains(&intent_status), || {
        Err(errors::ApiErrorResponse::PreconditionFailed {
            message: format!(
                "You cannot {action} this payment because it has status {intent_status}",
            ),
        })
    })
}

#[instrument(skip_all)]
pub(crate) fn validate_pm_or_token_given(
    payment_method: &Option<api_enums::PaymentMethod>,
    payment_method_data: &Option<api::PaymentMethodData>,
    payment_method_type: &Option<api_enums::PaymentMethodType>,
    mandate_type: &Option<api::MandateTransactionType>,
    token: &Option<String>,
    ctp_service_details: &Option<api_models::payments::CtpServiceDetails>,
) -> Result<(), errors::ApiErrorResponse> {
    utils::when(
        !matches!(
            payment_method_type,
            Some(api_enums::PaymentMethodType::Paypal)
        ) && !matches!(
            mandate_type,
            Some(api::MandateTransactionType::RecurringMandateTransaction)
        ) && token.is_none()
            && (payment_method_data.is_none() || payment_method.is_none())
            && ctp_service_details.is_none(),
        || {
            Err(errors::ApiErrorResponse::InvalidRequestData {
                message:
                    "A payment token or payment method data or ctp service details is required"
                        .to_string(),
            })
        },
    )
}

#[cfg(feature = "v2")]
// A function to perform database lookup and then verify the client secret
pub async fn verify_payment_intent_time_and_client_secret(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    client_secret: Option<String>,
) -> error_stack::Result<Option<PaymentIntent>, errors::ApiErrorResponse> {
    todo!()
}

#[cfg(feature = "v1")]
// A function to perform database lookup and then verify the client secret
pub async fn verify_payment_intent_time_and_client_secret(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    client_secret: Option<String>,
) -> error_stack::Result<Option<PaymentIntent>, errors::ApiErrorResponse> {
    let db = &*state.store;
    client_secret
        .async_map(|cs| async move {
            let payment_id = get_payment_id_from_client_secret(&cs)?;

            let payment_id = id_type::PaymentId::wrap(payment_id).change_context(
                errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "payment_id",
                },
            )?;

            #[cfg(feature = "v1")]
            let payment_intent = db
                .find_payment_intent_by_payment_id_merchant_id(
                    &state.into(),
                    &payment_id,
                    merchant_context.get_merchant_account().get_id(),
                    merchant_context.get_merchant_key_store(),
                    merchant_context.get_merchant_account().storage_scheme,
                )
                .await
                .change_context(errors::ApiErrorResponse::PaymentNotFound)?;

            #[cfg(feature = "v2")]
            let payment_intent = db
                .find_payment_intent_by_id(
                    &state.into(),
                    &payment_id,
                    key_store,
                    merchant_account.storage_scheme,
                )
                .await
                .change_context(errors::ApiErrorResponse::PaymentNotFound)?;

            authenticate_client_secret(Some(&cs), &payment_intent)?;
            Ok(payment_intent)
        })
        .await
        .transpose()
}

#[cfg(feature = "v1")]
/// Check whether the business details are configured in the merchant account
pub fn validate_business_details(
    business_country: Option<api_enums::CountryAlpha2>,
    business_label: Option<&String>,
    merchant_context: &domain::MerchantContext,
) -> RouterResult<()> {
    let primary_business_details = merchant_context
        .get_merchant_account()
        .primary_business_details
        .clone()
        .parse_value::<Vec<api_models::admin::PrimaryBusinessDetails>>("PrimaryBusinessDetails")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("failed to parse primary business details")?;

    business_country
        .zip(business_label)
        .map(|(business_country, business_label)| {
            primary_business_details
                .iter()
                .find(|business_details| {
                    &business_details.business == business_label
                        && business_details.country == business_country
                })
                .ok_or(errors::ApiErrorResponse::PreconditionFailed {
                    message: "business_details are not configured in the merchant account"
                        .to_string(),
                })
        })
        .transpose()?;

    Ok(())
}

#[inline]
pub(crate) fn get_payment_id_from_client_secret(cs: &str) -> RouterResult<String> {
    let (payment_id, _) = cs
        .rsplit_once("_secret_")
        .ok_or(errors::ApiErrorResponse::ClientSecretInvalid)?;
    Ok(payment_id.to_string())
}

#[cfg(feature = "v1")]
#[cfg(test)]
mod tests {
    #![allow(clippy::unwrap_used)]

    use super::*;

    #[test]
    fn test_authenticate_client_secret_session_not_expired() {
        let payment_intent = PaymentIntent {
            payment_id: id_type::PaymentId::try_from(Cow::Borrowed("23")).unwrap(),
            merchant_id: id_type::MerchantId::default(),
            status: storage_enums::IntentStatus::RequiresCapture,
            amount: MinorUnit::new(200),
            currency: None,
            amount_captured: None,
            customer_id: None,
            description: None,
            return_url: None,
            metadata: None,
            connector_id: None,
            shipping_address_id: None,
            billing_address_id: None,
            statement_descriptor_name: None,
            statement_descriptor_suffix: None,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            last_synced: None,
            setup_future_usage: None,
            fingerprint_id: None,
            off_session: None,
            client_secret: Some("1".to_string()),
            active_attempt: hyperswitch_domain_models::RemoteStorageObject::ForeignID(
                "nopes".to_string(),
            ),
            business_country: None,
            business_label: None,
            order_details: None,
            allowed_payment_method_types: None,
            connector_metadata: None,
            feature_metadata: None,
            attempt_count: 1,
            payment_link_id: None,
            profile_id: Some(common_utils::generate_profile_id_of_default_length()),
            merchant_decision: None,
            payment_confirm_source: None,
            surcharge_applicable: None,
            updated_by: storage_enums::MerchantStorageScheme::PostgresOnly.to_string(),
            request_incremental_authorization: Some(
                common_enums::RequestIncrementalAuthorization::default(),
            ),
            incremental_authorization_allowed: None,
            authorization_count: None,
            session_expiry: Some(
                common_utils::date_time::now()
                    .saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)),
            ),
            request_external_three_ds_authentication: None,
            split_payments: None,
            frm_metadata: None,
            customer_details: None,
            billing_details: None,
            merchant_order_reference_id: None,
            shipping_details: None,
            is_payment_processor_token_flow: None,
            organization_id: id_type::OrganizationId::default(),
            shipping_cost: None,
            tax_details: None,
            skip_external_tax_calculation: None,
            request_extended_authorization: None,
            psd2_sca_exemption_type: None,
            processor_merchant_id: id_type::MerchantId::default(),
            created_by: None,
            force_3ds_challenge: None,
            force_3ds_challenge_trigger: None,
            is_iframe_redirection_enabled: None,
            is_payment_id_from_merchant: None,
            payment_channel: None,
            tax_status: None,
            discount_amount: None,
            order_date: None,
            shipping_amount_tax: None,
            duty_amount: None,
            enable_partial_authorization: None,
        };
        let req_cs = Some("1".to_string());
        assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent).is_ok());
        // Check if the result is an Ok variant
    }

    #[test]
    fn test_authenticate_client_secret_session_expired() {
        let created_at =
            common_utils::date_time::now().saturating_sub(time::Duration::seconds(20 * 60));
        let payment_intent = PaymentIntent {
            payment_id: id_type::PaymentId::try_from(Cow::Borrowed("23")).unwrap(),
            merchant_id: id_type::MerchantId::default(),
            status: storage_enums::IntentStatus::RequiresCapture,
            amount: MinorUnit::new(200),
            currency: None,
            amount_captured: None,
            customer_id: None,
            description: None,
            return_url: None,
            metadata: None,
            connector_id: None,
            shipping_address_id: None,
            billing_address_id: None,
            statement_descriptor_name: None,
            statement_descriptor_suffix: None,
            created_at,
            modified_at: common_utils::date_time::now(),
            fingerprint_id: None,
            last_synced: None,
            setup_future_usage: None,
            off_session: None,
            client_secret: Some("1".to_string()),
            active_attempt: hyperswitch_domain_models::RemoteStorageObject::ForeignID(
                "nopes".to_string(),
            ),
            business_country: None,
            business_label: None,
            order_details: None,
            allowed_payment_method_types: None,
            connector_metadata: None,
            feature_metadata: None,
            attempt_count: 1,
            payment_link_id: None,
            profile_id: Some(common_utils::generate_profile_id_of_default_length()),
            merchant_decision: None,
            payment_confirm_source: None,
            surcharge_applicable: None,
            updated_by: storage_enums::MerchantStorageScheme::PostgresOnly.to_string(),
            request_incremental_authorization: Some(
                common_enums::RequestIncrementalAuthorization::default(),
            ),
            incremental_authorization_allowed: None,
            authorization_count: None,
            session_expiry: Some(
                created_at.saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)),
            ),
            request_external_three_ds_authentication: None,
            split_payments: None,
            frm_metadata: None,
            customer_details: None,
            billing_details: None,
            merchant_order_reference_id: None,
            shipping_details: None,
            is_payment_processor_token_flow: None,
            organization_id: id_type::OrganizationId::default(),
            shipping_cost: None,
            tax_details: None,
            skip_external_tax_calculation: None,
            request_extended_authorization: None,
            psd2_sca_exemption_type: None,
            processor_merchant_id: id_type::MerchantId::default(),
            created_by: None,
            force_3ds_challenge: None,
            force_3ds_challenge_trigger: None,
            is_iframe_redirection_enabled: None,
            is_payment_id_from_merchant: None,
            payment_channel: None,
            tax_status: None,
            discount_amount: None,
            order_date: None,
            shipping_amount_tax: None,
            duty_amount: None,
            enable_partial_authorization: None,
        };
        let req_cs = Some("1".to_string());
        assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent,).is_err())
    }

    #[test]
    fn test_authenticate_client_secret_expired() {
        let payment_intent = PaymentIntent {
            payment_id: id_type::PaymentId::try_from(Cow::Borrowed("23")).unwrap(),
            merchant_id: id_type::MerchantId::default(),
            status: storage_enums::IntentStatus::RequiresCapture,
            amount: MinorUnit::new(200),
            currency: None,
            amount_captured: None,
            customer_id: None,
            description: None,
            return_url: None,
            metadata: None,
            connector_id: None,
            shipping_address_id: None,
            billing_address_id: None,
            statement_descriptor_name: None,
            statement_descriptor_suffix: None,
            created_at: common_utils::date_time::now().saturating_sub(time::Duration::seconds(20)),
            modified_at: common_utils::date_time::now(),
            last_synced: None,
            setup_future_usage: None,
            off_session: None,
            client_secret: None,
            fingerprint_id: None,
            active_attempt: hyperswitch_domain_models::RemoteStorageObject::ForeignID(
                "nopes".to_string(),
            ),
            business_country: None,
            business_label: None,
            order_details: None,
            allowed_payment_method_types: None,
            connector_metadata: None,
            feature_metadata: None,
            attempt_count: 1,
            payment_link_id: None,
            profile_id: Some(common_utils::generate_profile_id_of_default_length()),
            merchant_decision: None,
            payment_confirm_source: None,
            surcharge_applicable: None,
            updated_by: storage_enums::MerchantStorageScheme::PostgresOnly.to_string(),
            request_incremental_authorization: Some(
                common_enums::RequestIncrementalAuthorization::default(),
            ),
            incremental_authorization_allowed: None,
            authorization_count: None,
            session_expiry: Some(
                common_utils::date_time::now()
                    .saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)),
            ),
            request_external_three_ds_authentication: None,
            split_payments: None,
            frm_metadata: None,
            customer_details: None,
            billing_details: None,
            merchant_order_reference_id: None,
            shipping_details: None,
            is_payment_processor_token_flow: None,
            organization_id: id_type::OrganizationId::default(),
            shipping_cost: None,
            tax_details: None,
            skip_external_tax_calculation: None,
            request_extended_authorization: None,
            psd2_sca_exemption_type: None,
            processor_merchant_id: id_type::MerchantId::default(),
            created_by: None,
            force_3ds_challenge: None,
            force_3ds_challenge_trigger: None,
            is_iframe_redirection_enabled: None,
            is_payment_id_from_merchant: None,
            payment_channel: None,
            tax_status: None,
            discount_amount: None,
            order_date: None,
            shipping_amount_tax: None,
            duty_amount: None,
            enable_partial_authorization: None,
        };
        let req_cs = Some("1".to_string());
        assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent).is_err())
    }
}

// This function will be removed after moving this functionality to server_wrap and using cache instead of config
#[instrument(skip_all)]
pub async fn insert_merchant_connector_creds_to_config(
    db: &dyn StorageInterface,
    merchant_id: &id_type::MerchantId,
    merchant_connector_details: admin::MerchantConnectorDetailsWrap,
) -> RouterResult<()> {
    if let Some(encoded_data) = merchant_connector_details.encoded_data {
        let redis = &db
            .get_redis_conn()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get redis connection")?;

        let key =
            merchant_id.get_creds_identifier_key(&merchant_connector_details.creds_identifier);

        redis
            .serialize_and_set_key_with_expiry(
                &key.as_str().into(),
                &encoded_data.peek(),
                consts::CONNECTOR_CREDS_TOKEN_TTL,
            )
            .await
            .map_or_else(
                |e| {
                    Err(e
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Failed to insert connector_creds to config"))
                },
                |_| Ok(()),
            )
    } else {
        Ok(())
    }
}

#[derive(Clone)]
pub enum MerchantConnectorAccountType {
    DbVal(Box<domain::MerchantConnectorAccount>),
    CacheVal(api_models::admin::MerchantConnectorDetails),
}

impl MerchantConnectorAccountType {
    pub fn get_metadata(&self) -> Option<masking::Secret<serde_json::Value>> {
        match self {
            Self::DbVal(val) => val.metadata.to_owned(),
            Self::CacheVal(val) => val.metadata.to_owned(),
        }
    }

    pub fn get_connector_account_details(&self) -> serde_json::Value {
        match self {
            Self::DbVal(val) => val.connector_account_details.peek().to_owned(),
            Self::CacheVal(val) => val.connector_account_details.peek().to_owned(),
        }
    }

    pub fn get_connector_wallets_details(&self) -> Option<masking::Secret<serde_json::Value>> {
        match self {
            Self::DbVal(val) => val.connector_wallets_details.as_deref().cloned(),
            Self::CacheVal(_) => None,
        }
    }

    pub fn is_disabled(&self) -> bool {
        match self {
            Self::DbVal(ref inner) => inner.disabled.unwrap_or(false),
            // Cached merchant connector account, only contains the account details,
            // the merchant connector account must only be cached if it's not disabled
            Self::CacheVal(_) => false,
        }
    }

    #[cfg(feature = "v1")]
    pub fn is_test_mode_on(&self) -> Option<bool> {
        match self {
            Self::DbVal(val) => val.test_mode,
            Self::CacheVal(_) => None,
        }
    }

    #[cfg(feature = "v2")]
    pub fn is_test_mode_on(&self) -> Option<bool> {
        None
    }

    pub fn get_mca_id(&self) -> Option<id_type::MerchantConnectorAccountId> {
        match self {
            Self::DbVal(db_val) => Some(db_val.get_id()),
            Self::CacheVal(_) => None,
        }
    }

    #[cfg(feature = "v1")]
    pub fn get_connector_name(&self) -> Option<String> {
        match self {
            Self::DbVal(db_val) => Some(db_val.connector_name.to_string()),
            Self::CacheVal(_) => None,
        }
    }

    #[cfg(feature = "v2")]
    pub fn get_connector_name(&self) -> Option<api_enums::Connector> {
        match self {
            Self::DbVal(db_val) => Some(db_val.connector_name),
            Self::CacheVal(_) => None,
        }
    }

    pub fn get_additional_merchant_data(
        &self,
    ) -> Option<Encryptable<masking::Secret<serde_json::Value>>> {
        match self {
            Self::DbVal(db_val) => db_val.additional_merchant_data.clone(),
            Self::CacheVal(_) => None,
        }
    }

    pub fn get_webhook_details(
        &self,
    ) -> CustomResult<Option<&masking::Secret<serde_json::Value>>, errors::ApiErrorResponse> {
        match self {
            Self::DbVal(db_val) => Ok(db_val.connector_webhook_details.as_ref()),
            Self::CacheVal(_) => Ok(None),
        }
    }
}

/// Query for merchant connector account either by business label or profile id
/// If profile_id is passed use it, or use connector_label to query merchant connector account
#[instrument(skip_all)]
pub async fn get_merchant_connector_account(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    creds_identifier: Option<&str>,
    key_store: &domain::MerchantKeyStore,
    profile_id: &id_type::ProfileId,
    connector_name: &str,
    merchant_connector_id: Option<&id_type::MerchantConnectorAccountId>,
) -> RouterResult<MerchantConnectorAccountType> {
    let db = &*state.store;
    let key_manager_state: &KeyManagerState = &state.into();
    match creds_identifier {
        Some(creds_identifier) => {
            let key = merchant_id.get_creds_identifier_key(creds_identifier);
            let cloned_key = key.clone();
            let redis_fetch = || async {
                db.get_redis_conn()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to get redis connection")
                    .async_and_then(|redis| async move {
                        redis
                            .get_and_deserialize_key(&key.as_str().into(), "String")
                            .await
                            .change_context(
                                errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                                    id: key.clone(),
                                },
                            )
                            .attach_printable(key.clone() + ": Not found in Redis")
                    })
                    .await
            };

            let db_fetch = || async {
                db.find_config_by_key(cloned_key.as_str())
                    .await
                    .to_not_found_response(
                        errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
                            id: cloned_key.to_owned(),
                        },
                    )
            };

            let mca_config: String = redis_fetch()
                .await
                .map_or_else(
                    |_| {
                        Either::Left(async {
                            match db_fetch().await {
                                Ok(config_entry) => Ok(config_entry.config),
                                Err(e) => Err(e),
                            }
                        })
                    },
                    |result| Either::Right(async { Ok(result) }),
                )
                .await?;

            let private_key = state
                .conf
                .jwekey
                .get_inner()
                .tunnel_private_key
                .peek()
                .as_bytes();

            let decrypted_mca = services::decrypt_jwe(mca_config.as_str(), services::KeyIdCheck::SkipKeyIdCheck, private_key, jwe::RSA_OAEP_256)
                                     .await
                                     .change_context(errors::ApiErrorResponse::UnprocessableEntity{
                                        message: "decoding merchant_connector_details failed due to invalid data format!".into()})
                                     .attach_printable(
                                        "Failed to decrypt merchant_connector_details sent in request and then put in cache",
                                    )?;

            let res = String::into_bytes(decrypted_mca)
                        .parse_struct("MerchantConnectorDetails")
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable(
                            "Failed to parse merchant_connector_details sent in request and then put in cache",
                        )?;

            Ok(MerchantConnectorAccountType::CacheVal(res))
        }
        None => {
            let mca: RouterResult<domain::MerchantConnectorAccount> =
                if let Some(merchant_connector_id) = merchant_connector_id {
                    #[cfg(feature = "v1")]
                    {
                        db.find_by_merchant_connector_account_merchant_id_merchant_connector_id(
                            key_manager_state,
                            merchant_id,
                            merchant_connector_id,
                            key_store,
                        )
                        .await
                        .to_not_found_response(
                            errors::ApiErrorResponse::MerchantConnectorAccountNotFound {


impl Capturable for PaymentsUpdateMetadataData {}

impl Capturable for for PaymentsUpdateMetadataData


OpenAPI Block Path: components.schemas.RealTimePaymentData
{
  "oneOf": [
    {
      "type": "object",
      "required": [
        "fps"
      ],
      "properties": {
        "fps": {
          "type": "object"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "duit_now"
      ],
      "properties": {
        "duit_now": {
          "type": "object"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "prompt_pay"
      ],
      "properties": {
        "prompt_pay": {
          "type": "object"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "viet_qr"
      ],
      "properties": {
        "viet_qr": {
          "type": "object"
        }
      }
    }
  ]
}


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