File: crates/router/src/services/api/generic_link_response.rs
 Public functions: 6

use common_utils::errors::CustomResult;
use error_stack::ResultExt;
use hyperswitch_domain_models::api::{
    GenericExpiredLinkData, GenericLinkFormData, GenericLinkStatusData, GenericLinksData,
};
use tera::{Context, Tera};

use super::build_secure_payment_link_html;
use crate::core::errors;
pub mod context;

pub fn build_generic_link_html(
    boxed_generic_link_data: GenericLinksData,
    locale: String,
) -> CustomResult<String, errors::ApiErrorResponse> {
    match boxed_generic_link_data {
        GenericLinksData::ExpiredLink(link_data) => build_generic_expired_link_html(&link_data),

        GenericLinksData::PaymentMethodCollect(pm_collect_data) => {
            build_pm_collect_link_html(&pm_collect_data)
        }
        GenericLinksData::PaymentMethodCollectStatus(pm_collect_data) => {
            build_pm_collect_link_status_html(&pm_collect_data)
        }
        GenericLinksData::PayoutLink(payout_link_data) => {
            build_payout_link_html(&payout_link_data, locale.as_str())
        }

        GenericLinksData::PayoutLinkStatus(pm_collect_data) => {
            build_payout_link_status_html(&pm_collect_data, locale.as_str())
        }
        GenericLinksData::SecurePaymentLink(payment_link_data) => {
            build_secure_payment_link_html(payment_link_data)
        }
    }
}

pub fn build_generic_expired_link_html(
    link_data: &GenericExpiredLinkData,
) -> CustomResult<String, errors::ApiErrorResponse> {
    let mut tera = Tera::default();
    let mut context = Context::new();

    // Build HTML
    let html_template = include_str!("../../core/generic_link/expired_link/index.html").to_string();
    let _ = tera.add_raw_template("generic_expired_link", &html_template);
    context.insert("title", &link_data.title);
    context.insert("message", &link_data.message);
    context.insert("theme", &link_data.theme);

    tera.render("generic_expired_link", &context)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render expired link HTML template")
}

fn build_html_template(
    link_data: &GenericLinkFormData,
    document: &'static str,
    styles: &'static str,
) -> CustomResult<(Tera, Context), errors::ApiErrorResponse> {
    let mut tera: Tera = Tera::default();
    let mut context = Context::new();

    // Insert dynamic context in CSS
    let css_dynamic_context = "{{ color_scheme }}";
    let css_template = styles.to_string();
    let final_css = format!("{css_dynamic_context}\n{css_template}");
    let _ = tera.add_raw_template("document_styles", &final_css);
    context.insert("color_scheme", &link_data.css_data);

    let css_style_tag = tera
        .render("document_styles", &context)
        .map(|css| format!("<style>{css}</style>"))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render CSS template")?;

    // Insert HTML context
    let html_template = document.to_string();
    let _ = tera.add_raw_template("html_template", &html_template);
    context.insert("css_style_tag", &css_style_tag);

    Ok((tera, context))
}

pub fn build_payout_link_html(
    link_data: &GenericLinkFormData,
    locale: &str,
) -> CustomResult<String, errors::ApiErrorResponse> {
    let document = include_str!("../../core/generic_link/payout_link/initiate/index.html");
    let styles = include_str!("../../core/generic_link/payout_link/initiate/styles.css");
    let (mut tera, mut context) = build_html_template(link_data, document, styles)
        .attach_printable("Failed to build context for payout link's HTML template")?;

    // Insert dynamic context in JS
    let script = include_str!("../../core/generic_link/payout_link/initiate/script.js");
    let js_template = script.to_string();
    let js_dynamic_context = "{{ script_data }}";
    let final_js = format!("{js_dynamic_context}\n{js_template}");
    let _ = tera.add_raw_template("document_scripts", &final_js);
    context.insert("script_data", &link_data.js_data);
    context::insert_locales_in_context_for_payout_link(&mut context, locale);
    let js_script_tag = tera
        .render("document_scripts", &context)
        .map(|js| format!("<script>{js}</script>"))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render JS template")?;
    context.insert("js_script_tag", &js_script_tag);
    context.insert(
        "hyper_sdk_loader_script_tag",
        &format!(
            r#"<script src="{}" onload="initializePayoutSDK()"></script>"#,
            link_data.sdk_url
        ),
    );

    // Render HTML template
    tera.render("html_template", &context)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render payout link's HTML template")
}

pub fn build_pm_collect_link_html(
    link_data: &GenericLinkFormData,
) -> CustomResult<String, errors::ApiErrorResponse> {
    let document =
        include_str!("../../core/generic_link/payment_method_collect/initiate/index.html");
    let styles = include_str!("../../core/generic_link/payment_method_collect/initiate/styles.css");
    let (mut tera, mut context) = build_html_template(link_data, document, styles)
        .attach_printable(
            "Failed to build context for payment method collect link's HTML template",
        )?;

    // Insert dynamic context in JS
    let script = include_str!("../../core/generic_link/payment_method_collect/initiate/script.js");
    let js_template = script.to_string();
    let js_dynamic_context = "{{ script_data }}";
    let final_js = format!("{js_dynamic_context}\n{js_template}");
    let _ = tera.add_raw_template("document_scripts", &final_js);
    context.insert("script_data", &link_data.js_data);
    let js_script_tag = tera
        .render("document_scripts", &context)
        .map(|js| format!("<script>{js}</script>"))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render JS template")?;
    context.insert("js_script_tag", &js_script_tag);
    context.insert(
        "hyper_sdk_loader_script_tag",
        &format!(
            r#"<script src="{}" onload="initializeCollectSDK()"></script>"#,
            link_data.sdk_url
        ),
    );

    // Render HTML template
    tera.render("html_template", &context)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render payment method collect link's HTML template")
}

pub fn build_payout_link_status_html(
    link_data: &GenericLinkStatusData,
    locale: &str,
) -> CustomResult<String, errors::ApiErrorResponse> {
    let mut tera = Tera::default();
    let mut context = Context::new();

    // Insert dynamic context in CSS
    let css_dynamic_context = "{{ color_scheme }}";
    let css_template =
        include_str!("../../core/generic_link/payout_link/status/styles.css").to_string();
    let final_css = format!("{css_dynamic_context}\n{css_template}");
    let _ = tera.add_raw_template("payout_link_status_styles", &final_css);
    context.insert("color_scheme", &link_data.css_data);

    let css_style_tag = tera
        .render("payout_link_status_styles", &context)
        .map(|css| format!("<style>{css}</style>"))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render payout link status CSS template")?;

    // Insert dynamic context in JS
    let js_dynamic_context = "{{ script_data }}";
    let js_template =
        include_str!("../../core/generic_link/payout_link/status/script.js").to_string();
    let final_js = format!("{js_dynamic_context}\n{js_template}");
    let _ = tera.add_raw_template("payout_link_status_script", &final_js);
    context.insert("script_data", &link_data.js_data);
    context::insert_locales_in_context_for_payout_link_status(&mut context, locale);
    let js_script_tag = tera
        .render("payout_link_status_script", &context)
        .map(|js| format!("<script>{js}</script>"))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render payout link status JS template")?;

    // Build HTML
    let html_template =
        include_str!("../../core/generic_link/payout_link/status/index.html").to_string();
    let _ = tera.add_raw_template("payout_status_link", &html_template);
    context.insert("css_style_tag", &css_style_tag);
    context.insert("js_script_tag", &js_script_tag);

    tera.render("payout_status_link", &context)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render payout link status HTML template")
}

pub fn build_pm_collect_link_status_html(
    link_data: &GenericLinkStatusData,
) -> CustomResult<String, errors::ApiErrorResponse> {
    let mut tera = Tera::default();
    let mut context = Context::new();

    // Insert dynamic context in CSS
    let css_dynamic_context = "{{ color_scheme }}";
    let css_template =
        include_str!("../../core/generic_link/payment_method_collect/status/styles.css")
            .to_string();
    let final_css = format!("{css_dynamic_context}\n{css_template}");
    let _ = tera.add_raw_template("pm_collect_link_status_styles", &final_css);
    context.insert("color_scheme", &link_data.css_data);

    let css_style_tag = tera
        .render("pm_collect_link_status_styles", &context)
        .map(|css| format!("<style>{css}</style>"))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render payment method collect link status CSS template")?;

    // Insert dynamic context in JS
    let js_dynamic_context = "{{ collect_link_status_context }}";
    let js_template =
        include_str!("../../core/generic_link/payment_method_collect/status/script.js").to_string();
    let final_js = format!("{js_dynamic_context}\n{js_template}");
    let _ = tera.add_raw_template("pm_collect_link_status_script", &final_js);
    context.insert("collect_link_status_context", &link_data.js_data);

    let js_script_tag = tera
        .render("pm_collect_link_status_script", &context)
        .map(|js| format!("<script>{js}</script>"))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render payment method collect link status JS template")?;

    // Build HTML
    let html_template =
        include_str!("../../core/generic_link/payment_method_collect/status/index.html")
            .to_string();
    let _ = tera.add_raw_template("payment_method_collect_status_link", &html_template);
    context.insert("css_style_tag", &css_style_tag);
    context.insert("js_script_tag", &js_script_tag);

    tera.render("payment_method_collect_status_link", &context)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to render payment method collect link status HTML template")
}


pub struct Powertranz;


OpenAPI Block Path: components.schemas.DisputeResponsePaymentsRetrieve
{
  "type": "object",
  "required": [
    "dispute_id",
    "dispute_stage",
    "dispute_status",
    "connector_status",
    "connector_dispute_id",
    "created_at"
  ],
  "properties": {
    "dispute_id": {
      "type": "string",
      "description": "The identifier for dispute"
    },
    "dispute_stage": {
      "$ref": "#/components/schemas/DisputeStage"
    },
    "dispute_status": {
      "$ref": "#/components/schemas/DisputeStatus"
    },
    "connector_status": {
      "type": "string",
      "description": "Status of the dispute sent by connector"
    },
    "connector_dispute_id": {
      "type": "string",
      "description": "Dispute id sent by connector"
    },
    "connector_reason": {
      "type": "string",
      "description": "Reason of dispute sent by connector",
      "nullable": true
    },
    "connector_reason_code": {
      "type": "string",
      "description": "Reason code of dispute sent by connector",
      "nullable": true
    },
    "challenge_required_by": {
      "type": "string",
      "format": "date-time",
      "description": "Evidence deadline of dispute sent by connector",
      "nullable": true
    },
    "connector_created_at": {
      "type": "string",
      "format": "date-time",
      "description": "Dispute created time sent by connector",
      "nullable": true
    },
    "connector_updated_at": {
      "type": "string",
      "format": "date-time",
      "description": "Dispute updated time sent by connector",
      "nullable": true
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "description": "Time at which dispute is received"
    }
  }
}


Web Documentation: Intelligent Routing | Hyperswitch
# Type: Web Doc

The Hyperswitch Intelligent Routing module augments your payment processing by dynamically switching between processors in real-time for every transaction to optimally maximise first attempt auth rates and minimise processing cost.
Types of Intelligent Routing
Hyperswitch supports four intelligent routing strategies:
Auth Rate Based Routing:
Uses real-time success rates and ML-driven optimisation to route transactions to the best-performing gateway.
The Auth Rates for the payments are tracked at a granular level of payment parameters like payment method, payment method type, amount, currency, authentication type, card network etc.
The model
uses a Multi-Armed Bandit (MAB) problem with Delayed Feedback, where each Gateway is an "arm" with fluctuating success rates and varying latency for success and failure. The approach used to solve this problem is driven by
explore-exploit
strategy.
Exploration:
We continuously evaluate all gateways by sending a small percentage of traffic to ensure up-to-date performance data.
Exploitation:
We continuously route most traffic to the best-performing Gateway to maximise the overall success rate.
The sensitivity of the system can be tweaked by the merchants by configuring settings such as Bucket Sizes, Parameters to be considered and Hedging Percentage
The hedging percentage decides the exploration factor of the model
Least Cost Routing:
Picks the least cost network for every transaction basis the availability of back-of-the-card network and processor compatibility
Elimination Routing:
Tracks acute incidents such as downtimes and technical errors to de-prioritise gateways. This will be used as a final check after other routing logics are applied.
Contracts-Based Routing:
Distributes payments across processors to meet contractual volume commitments. [BETA FEATURE]
Architecture Diagram
Self Deploy the Routing Engine
Using Auth Rate based Routing for Hyperswitch
29 days ago
Was this helpful?

Intelligent Routing | Hyperswitch


impl Parse for FieldMeta {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let _meta_type: Ident = input.parse()?;
        input.parse::<syn::Token![=]>()?;
        let value: Ident = input.parse()?;
        Ok(Self { _meta_type, value })
    }
}


pub struct PaymentData {
    #[serde(rename = "merchant-account-id")]
    pub merchant_account_id: MerchantAccountId,
    #[serde(rename = "request-id")]
    pub request_id: String,
    #[serde(rename = "transaction-type")]
    pub transaction_type: GetnetTransactionType,
    #[serde(rename = "requested-amount")]
    pub requested_amount: Amount,
    #[serde(rename = "account-holder")]
    pub account_holder: Option<AccountHolder>,
    pub card: Card,
    #[serde(rename = "ip-address")]
    pub ip_address: Option<Secret<String, IpAddress>>,
    #[serde(rename = "payment-methods")]
    pub payment_methods: PaymentMethodContainer,
    pub notifications: Option<NotificationContainer>,
}


impl RevenueRecoveryAttempt {
    pub async fn load_recovery_attempt_from_api(
        data: api_models::payments::RecoveryPaymentsCreate,
        state: &SessionState,
        req_state: &ReqState,
        merchant_context: &domain::MerchantContext,
        profile: &domain::Profile,
        payment_intent: revenue_recovery::RecoveryPaymentIntent,
        payment_merchant_connector_account: domain::MerchantConnectorAccount,
    ) -> CustomResult<
        (
            revenue_recovery::RecoveryPaymentAttempt,
            revenue_recovery::RecoveryPaymentIntent,
        ),
        errors::RevenueRecoveryError,
    > {
        let recovery_attempt = Self(revenue_recovery::RevenueRecoveryAttemptData::foreign_from(
            &data,
        ));
        recovery_attempt
            .get_payment_attempt(state, req_state, merchant_context, profile, &payment_intent)
            .await
            .transpose()
            .async_unwrap_or_else(|| async {
                recovery_attempt
                    .record_payment_attempt(
                        state,
                        req_state,
                        merchant_context,
                        profile,
                        &payment_intent,
                        &data.billing_merchant_connector_id,
                        Some(payment_merchant_connector_account),
                    )
                    .await
            })
            .await
    }

    fn get_recovery_invoice_transaction_details(
        connector_enum: &connector_integration_interface::ConnectorEnum,
        request_details: &hyperswitch_interfaces::webhooks::IncomingWebhookRequestDetails<'_>,
        billing_connector_payment_details: Option<
            &revenue_recovery_response::BillingConnectorPaymentsSyncResponse,
        >,
        billing_connector_invoice_details: &revenue_recovery::RevenueRecoveryInvoiceData,
    ) -> CustomResult<Self, errors::RevenueRecoveryError> {
        billing_connector_payment_details.map_or_else(
            || {
                interface_webhooks::IncomingWebhook::get_revenue_recovery_attempt_details(
                    connector_enum,
                    request_details,
                )
                .change_context(errors::RevenueRecoveryError::TransactionWebhookProcessingFailed)
                .attach_printable(
                    "Failed to get recovery attempt details from the billing connector",
                )
                .map(RevenueRecoveryAttempt)
            },
            |data| {
                Ok(Self(revenue_recovery::RevenueRecoveryAttemptData::from((
                    data,
                    billing_connector_invoice_details,
                ))))
            },
        )
    }
    pub fn get_revenue_recovery_attempt(
        payment_intent: &domain_payments::PaymentIntent,
        revenue_recovery_metadata: &api_payments::PaymentRevenueRecoveryMetadata,
        billing_connector_account: &domain::MerchantConnectorAccount,
        card_info: api_payments::AdditionalCardInfo,
        payment_processor_token: &str,
    ) -> CustomResult<Self, errors::RevenueRecoveryError> {
        let revenue_recovery_data = payment_intent
            .create_revenue_recovery_attempt_data(
                revenue_recovery_metadata.clone(),
                billing_connector_account,
                card_info,
                payment_processor_token,
            )
            .change_context(errors::RevenueRecoveryError::RevenueRecoveryAttemptDataCreateFailed)
            .attach_printable("Failed to build recovery attempt data")?;
        Ok(Self(revenue_recovery_data))
    }
    async fn get_payment_attempt(
        &self,
        state: &SessionState,
        req_state: &ReqState,
        merchant_context: &domain::MerchantContext,
        profile: &domain::Profile,
        payment_intent: &revenue_recovery::RecoveryPaymentIntent,
    ) -> CustomResult<
        Option<(
            revenue_recovery::RecoveryPaymentAttempt,
            revenue_recovery::RecoveryPaymentIntent,
        )>,
        errors::RevenueRecoveryError,
    > {
        let attempt_response =
            Box::pin(payments::payments_list_attempts_using_payment_intent_id::<
                payments::operations::PaymentGetListAttempts,
                api_payments::PaymentAttemptListResponse,
                _,
                payments::operations::payment_attempt_list::PaymentGetListAttempts,
                hyperswitch_domain_models::payments::PaymentAttemptListData<
                    payments::operations::PaymentGetListAttempts,
                >,
            >(
                state.clone(),
                req_state.clone(),
                merchant_context.clone(),
                profile.clone(),
                payments::operations::PaymentGetListAttempts,
                api_payments::PaymentAttemptListRequest {
                    payment_intent_id: payment_intent.payment_id.clone(),
                },
                payment_intent.payment_id.clone(),
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            ))
            .await;
        let response = match attempt_response {
            Ok(services::ApplicationResponse::JsonWithHeaders((payments_response, _))) => {
                let final_attempt = self
                    .0
                    .charge_id
                    .as_ref()
                    .map(|charge_id| {
                        payments_response
                            .find_attempt_in_attempts_list_using_charge_id(charge_id.clone())
                    })
                    .unwrap_or_else(|| {
                        self.0
                            .connector_transaction_id
                            .as_ref()
                            .and_then(|transaction_id| {
                                payments_response
                                    .find_attempt_in_attempts_list_using_connector_transaction_id(
                                        transaction_id,
                                    )
                            })
                    });
                let payment_attempt =
                    final_attempt.map(|res| revenue_recovery::RecoveryPaymentAttempt {
                        attempt_id: res.id.to_owned(),
                        attempt_status: res.status.to_owned(),
                        feature_metadata: res.feature_metadata.to_owned(),
                        amount: res.amount.net_amount,
                        network_advice_code: res.error.clone().and_then(|e| e.network_advice_code), // Placeholder, to be populated if available
                        network_decline_code: res
                            .error
                            .clone()
                            .and_then(|e| e.network_decline_code), // Placeholder, to be populated if available
                        error_code: res.error.clone().map(|error| error.code),
                        created_at: res.created_at,
                    });
                // If we have an attempt, combine it with payment_intent in a tuple.
                let res_with_payment_intent_and_attempt =
                    payment_attempt.map(|attempt| (attempt, (*payment_intent).clone()));
                Ok(res_with_payment_intent_and_attempt)
            }
            Ok(_) => Err(errors::RevenueRecoveryError::PaymentAttemptFetchFailed)
                .attach_printable("Unexpected response from payment intent core"),
            error @ Err(_) => {
                logger::error!(?error);
                Err(errors::RevenueRecoveryError::PaymentAttemptFetchFailed)
                    .attach_printable("failed to fetch payment attempt in recovery webhook flow")
            }
        }?;
        Ok(response)
    }

    #[allow(clippy::too_many_arguments)]
    async fn record_payment_attempt(
        &self,
        state: &SessionState,
        req_state: &ReqState,
        merchant_context: &domain::MerchantContext,
        profile: &domain::Profile,
        payment_intent: &revenue_recovery::RecoveryPaymentIntent,
        billing_connector_account_id: &id_type::MerchantConnectorAccountId,
        payment_connector_account: Option<domain::MerchantConnectorAccount>,
    ) -> CustomResult<
        (
            revenue_recovery::RecoveryPaymentAttempt,
            revenue_recovery::RecoveryPaymentIntent,
        ),
        errors::RevenueRecoveryError,
    > {
        let payment_connector_id =   payment_connector_account.as_ref().map(|account: &hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccount| account.id.clone());
        let payment_connector_name = payment_connector_account
            .as_ref()
            .map(|account| account.connector_name);
        let request_payload: api_payments::PaymentsAttemptRecordRequest = self
            .create_payment_record_request(
                state,
                billing_connector_account_id,
                payment_connector_id,
                payment_connector_name,
                common_enums::TriggeredBy::External,
            )
            .await?;
        let attempt_response = Box::pin(payments::record_attempt_core(
            state.clone(),
            req_state.clone(),
            merchant_context.clone(),
            profile.clone(),
            request_payload,
            payment_intent.payment_id.clone(),
            hyperswitch_domain_models::payments::HeaderPayload::default(),
        ))
        .await;

        let (recovery_attempt, updated_recovery_intent) = match attempt_response {
            Ok(services::ApplicationResponse::JsonWithHeaders((attempt_response, _))) => {
                Ok((
                    revenue_recovery::RecoveryPaymentAttempt {
                        attempt_id: attempt_response.id.clone(),
                        attempt_status: attempt_response.status,
                        feature_metadata: attempt_response.payment_attempt_feature_metadata,
                        amount: attempt_response.amount,
                        network_advice_code: attempt_response
                            .error_details
                            .clone()
                            .and_then(|error| error.network_decline_code), // Placeholder, to be populated if available
                        network_decline_code: attempt_response
                            .error_details
                            .clone()
                            .and_then(|error| error.network_decline_code), // Placeholder, to be populated if available
                        error_code: attempt_response
                            .error_details
                            .clone()
                            .map(|error| error.code),
                        created_at: attempt_response.created_at,
                    },
                    revenue_recovery::RecoveryPaymentIntent {
                        payment_id: payment_intent.payment_id.clone(),
                        status: attempt_response.status.into(), // Using status from attempt_response
                        feature_metadata: attempt_response.payment_intent_feature_metadata, // Using feature_metadata from attempt_response
                        merchant_id: payment_intent.merchant_id.clone(),
                        merchant_reference_id: payment_intent.merchant_reference_id.clone(),
                        invoice_amount: payment_intent.invoice_amount,
                        invoice_currency: payment_intent.invoice_currency,
                        created_at: payment_intent.created_at,
                        billing_address: payment_intent.billing_address.clone(),
                    },
                ))
            }
            Ok(_) => Err(errors::RevenueRecoveryError::PaymentAttemptFetchFailed)
                .attach_printable("Unexpected response from record attempt core"),
            error @ Err(_) => {
                logger::error!(?error);
                Err(errors::RevenueRecoveryError::PaymentAttemptFetchFailed)
                    .attach_printable("failed to record attempt in recovery webhook flow")
            }
        }?;

        let response = (recovery_attempt, updated_recovery_intent);

        self.store_payment_processor_tokens_in_redis(state, &response.0, payment_connector_name)
            .await
            .map_err(|e| {
                router_env::logger::error!(
                    "Failed to store payment processor tokens in Redis: {:?}",
                    e
                );
                errors::RevenueRecoveryError::RevenueRecoveryRedisInsertFailed
            })?;

        Ok(response)
    }

    pub async fn create_payment_record_request(
        &self,
        state: &SessionState,
        billing_merchant_connector_account_id: &id_type::MerchantConnectorAccountId,
        payment_merchant_connector_account_id: Option<id_type::MerchantConnectorAccountId>,
        payment_connector: Option<common_enums::connector_enums::Connector>,
        triggered_by: common_enums::TriggeredBy,
    ) -> CustomResult<api_payments::PaymentsAttemptRecordRequest, errors::RevenueRecoveryError>
    {
        let revenue_recovery_attempt_data = &self.0;
        let amount_details =
            api_payments::PaymentAttemptAmountDetails::from(revenue_recovery_attempt_data);
        let feature_metadata = api_payments::PaymentAttemptFeatureMetadata {
            revenue_recovery: Some(api_payments::PaymentAttemptRevenueRecoveryData {
                // Since we are recording the external paymenmt attempt, this is hardcoded to External
                attempt_triggered_by: triggered_by,
                charge_id: self.0.charge_id.clone(),
            }),
        };

        let card_info = revenue_recovery_attempt_data
            .card_info
            .card_isin
            .clone()
            .async_and_then(|isin| async move {
                let issuer_identifier_number = isin.clone();
                state
                    .store
                    .get_card_info(issuer_identifier_number.as_str())
                    .await
                    .map_err(|error| services::logger::warn!(card_info_error=?error))
                    .ok()
            })
            .await
            .flatten();
        let payment_method_data = api_models::payments::RecordAttemptPaymentMethodDataRequest {
            payment_method_data: api_models::payments::AdditionalPaymentData::Card(Box::new(
                revenue_recovery_attempt_data.card_info.clone(),
            )),
            billing: None,
        };

        let card_issuer = revenue_recovery_attempt_data.card_info.card_issuer.clone();

        let error =
            Option::<api_payments::RecordAttemptErrorDetails>::from(revenue_recovery_attempt_data);
        Ok(api_payments::PaymentsAttemptRecordRequest {
            amount_details,
            status: revenue_recovery_attempt_data.status,
            billing: None,
            shipping: None,
            connector: payment_connector,
            payment_merchant_connector_id: payment_merchant_connector_account_id,
            error,
            description: None,
            connector_transaction_id: revenue_recovery_attempt_data
                .connector_transaction_id
                .clone(),
            payment_method_type: revenue_recovery_attempt_data.payment_method_type,
            billing_connector_id: billing_merchant_connector_account_id.clone(),
            payment_method_subtype: revenue_recovery_attempt_data.payment_method_sub_type,
            payment_method_data: Some(payment_method_data),
            metadata: None,
            feature_metadata: Some(feature_metadata),
            transaction_created_at: revenue_recovery_attempt_data.transaction_created_at,
            processor_payment_method_token: revenue_recovery_attempt_data
                .processor_payment_method_token
                .clone(),
            connector_customer_id: revenue_recovery_attempt_data.connector_customer_id.clone(),
            retry_count: revenue_recovery_attempt_data.retry_count,
            invoice_next_billing_time: revenue_recovery_attempt_data.invoice_next_billing_time,
            invoice_billing_started_at_time: revenue_recovery_attempt_data
                .invoice_billing_started_at_time,
            triggered_by,
            card_network: revenue_recovery_attempt_data.card_info.card_network.clone(),
            card_issuer,
        })
    }

    pub async fn find_payment_merchant_connector_account(
        &self,
        state: &SessionState,
        key_store: &domain::MerchantKeyStore,
        billing_connector_account: &domain::MerchantConnectorAccount,
    ) -> CustomResult<Option<domain::MerchantConnectorAccount>, errors::RevenueRecoveryError> {
        let payment_merchant_connector_account_id = billing_connector_account
            .get_payment_merchant_connector_account_id_using_account_reference_id(
                self.0.connector_account_reference_id.clone(),
            );
        let db = &*state.store;
        let key_manager_state = &(state).into();
        let payment_merchant_connector_account = payment_merchant_connector_account_id
            .as_ref()
            .async_map(|mca_id| async move {
                db.find_merchant_connector_account_by_id(key_manager_state, mca_id, key_store)
                    .await
            })
            .await
            .transpose()
            .change_context(errors::RevenueRecoveryError::PaymentMerchantConnectorAccountNotFound)
            .attach_printable(
                "failed to fetch payment merchant connector id using account reference id",
            )?;
        Ok(payment_merchant_connector_account)
    }

    #[allow(clippy::too_many_arguments)]
    async fn get_recovery_payment_attempt(
        is_recovery_transaction_event: bool,
        billing_connector_account: &domain::MerchantConnectorAccount,
        state: &SessionState,
        connector_enum: &connector_integration_interface::ConnectorEnum,
        req_state: &ReqState,
        billing_connector_payment_details: Option<
            &revenue_recovery_response::BillingConnectorPaymentsSyncResponse,
        >,
        request_details: &hyperswitch_interfaces::webhooks::IncomingWebhookRequestDetails<'_>,
        merchant_context: &domain::MerchantContext,
        business_profile: &domain::Profile,
        payment_intent: &revenue_recovery::RecoveryPaymentIntent,
        invoice_details: &revenue_recovery::RevenueRecoveryInvoiceData,
    ) -> CustomResult<
        (
            Option<revenue_recovery::RecoveryPaymentAttempt>,
            revenue_recovery::RecoveryPaymentIntent,
        ),
        errors::RevenueRecoveryError,
    > {
        let payment_attempt_with_recovery_intent = match is_recovery_transaction_event {
            true => {
                let invoice_transaction_details = Self::get_recovery_invoice_transaction_details(
                    connector_enum,
                    request_details,
                    billing_connector_payment_details,
                    invoice_details,
                )?;

                // Find the payment merchant connector ID at the top level to avoid multiple DB calls.
                let payment_merchant_connector_account = invoice_transaction_details
                    .find_payment_merchant_connector_account(
                        state,
                        merchant_context.get_merchant_key_store(),
                        billing_connector_account,
                    )
                    .await?;

                let (payment_attempt, updated_payment_intent) = invoice_transaction_details
                    .get_payment_attempt(
                        state,
                        req_state,
                        merchant_context,
                        business_profile,
                        payment_intent,
                    )
                    .await
                    .transpose()
                    .async_unwrap_or_else(|| async {
                        invoice_transaction_details
                            .record_payment_attempt(
                                state,
                                req_state,
                                merchant_context,
                                business_profile,
                                payment_intent,
                                &billing_connector_account.get_id(),
                                payment_merchant_connector_account,
                            )
                            .await
                    })
                    .await?;
                (Some(payment_attempt), updated_payment_intent)
            }

            false => (None, payment_intent.clone()),
        };

        Ok(payment_attempt_with_recovery_intent)
    }

    /// Store payment processor tokens in Redis for retry management
    async fn store_payment_processor_tokens_in_redis(
        &self,
        state: &SessionState,
        recovery_attempt: &revenue_recovery::RecoveryPaymentAttempt,
        payment_connector_name: Option<common_enums::connector_enums::Connector>,
    ) -> CustomResult<(), errors::RevenueRecoveryError> {
        let revenue_recovery_attempt_data = &self.0;
        let error_code = revenue_recovery_attempt_data.error_code.clone();
        let error_message = revenue_recovery_attempt_data.error_message.clone();
        let connector_name = payment_connector_name
            .ok_or(errors::RevenueRecoveryError::TransactionWebhookProcessingFailed)
            .attach_printable("unable to derive payment connector")?
            .to_string();

        let gsm_record = helpers::get_gsm_record(
            state,
            error_code.clone(),
            error_message,
            connector_name,
            REVENUE_RECOVERY.to_string(),
        )
        .await;

        let is_hard_decline = gsm_record
            .and_then(|record| record.error_category)
            .map(|category| category == common_enums::ErrorCategory::HardDecline)
            .unwrap_or(false);

        // Extract required fields from the revenue recovery attempt data
        let connector_customer_id = revenue_recovery_attempt_data.connector_customer_id.clone();

        let attempt_id = recovery_attempt.attempt_id.clone();
        let token_unit = PaymentProcessorTokenStatus {
            error_code,
            inserted_by_attempt_id: attempt_id.clone(),
            daily_retry_history: HashMap::from([(recovery_attempt.created_at.date(), 1)]),
            scheduled_at: None,
            is_hard_decline: Some(is_hard_decline),
            payment_processor_token_details: PaymentProcessorTokenDetails {
                payment_processor_token: revenue_recovery_attempt_data
                    .processor_payment_method_token
                    .clone(),
                expiry_month: revenue_recovery_attempt_data
                    .card_info
                    .card_exp_month
                    .clone(),
                expiry_year: revenue_recovery_attempt_data
                    .card_info
                    .card_exp_year
                    .clone(),
                card_issuer: revenue_recovery_attempt_data.card_info.card_issuer.clone(),
                last_four_digits: revenue_recovery_attempt_data.card_info.last4.clone(),
                card_network: revenue_recovery_attempt_data.card_info.card_network.clone(),
                card_type: revenue_recovery_attempt_data.card_info.card_type.clone(),
            },
        };

        // Make the Redis call to store tokens
        RedisTokenManager::upsert_payment_processor_token(
            state,
            &connector_customer_id,
            token_unit,
        )
        .await
        .change_context(errors::RevenueRecoveryError::RevenueRecoveryRedisInsertFailed)
        .attach_printable("Failed to store payment processor tokens in Redis")?;

        Ok(())
    }
}


pub struct GlobalpayErrorResponse {
    pub error_code: String,
    pub detailed_error_code: String,
    pub detailed_error_description: String,
}


pub struct NoonBilling {
    address: NoonBillingAddress,
}


impl Helcim {
    pub fn new() -> &'static Self {
        &Self {
            amount_convertor: &FloatMajorUnitForConnector,
        }
    }
}


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


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


File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service.rs
 Public functions: 1
 Public structs: 1

pub mod transformers;

use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    router_data::{AccessToken, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
        Authenticate, AuthenticationConfirmation, PostAuthenticate, PreAuthenticate,
    },
    router_request_types::{
        unified_authentication_service::{
            UasAuthenticationRequestData, UasAuthenticationResponseData,
            UasConfirmationRequestData, UasPostAuthenticationRequestData,
            UasPreAuthenticationRequestData,
        },
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{
        UasAuthenticationConfirmationRouterData, UasAuthenticationRouterData,
        UasPostAuthenticationRouterData, UasPreAuthenticationRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    consts::NO_ERROR_MESSAGE,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use transformers as unified_authentication_service;

use crate::{constants::headers, types::ResponseRouterData, utils};

#[derive(Clone)]
pub struct UnifiedAuthenticationService {
    amount_converter: &'static (dyn AmountConvertor<Output = FloatMajorUnit> + Sync),
}

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

impl api::Payment for UnifiedAuthenticationService {}
impl api::PaymentSession for UnifiedAuthenticationService {}
impl api::ConnectorAccessToken for UnifiedAuthenticationService {}
impl api::MandateSetup for UnifiedAuthenticationService {}
impl api::PaymentAuthorize for UnifiedAuthenticationService {}
impl api::PaymentSync for UnifiedAuthenticationService {}
impl api::PaymentCapture for UnifiedAuthenticationService {}
impl api::PaymentVoid for UnifiedAuthenticationService {}
impl api::Refund for UnifiedAuthenticationService {}
impl api::RefundExecute for UnifiedAuthenticationService {}
impl api::RefundSync for UnifiedAuthenticationService {}
impl api::PaymentToken for UnifiedAuthenticationService {}
impl api::UnifiedAuthenticationService for UnifiedAuthenticationService {}
impl api::UasPreAuthentication for UnifiedAuthenticationService {}
impl api::UasPostAuthentication for UnifiedAuthenticationService {}
impl api::UasAuthenticationConfirmation for UnifiedAuthenticationService {}
impl api::UasAuthentication for UnifiedAuthenticationService {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for UnifiedAuthenticationService
{
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData>
    for UnifiedAuthenticationService
{
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response>
    for UnifiedAuthenticationService
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        _req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let header = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                self.get_content_type().to_string().into(),
            ),
            (
                headers::SOURCE.to_string(),
                self.get_content_type().to_string().into(),
            ),
        ];
        Ok(header)
    }
}

impl ConnectorCommon for UnifiedAuthenticationService {
    fn id(&self) -> &'static str {
        "unified_authentication_service"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Base
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.unified_authentication_service.base_url.as_ref()
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: unified_authentication_service::UnifiedAuthenticationServiceErrorResponse =
            res.response
                .parse_struct("UnifiedAuthenticationServiceErrorResponse")
                .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error.clone(),
            message: NO_ERROR_MESSAGE.to_owned(),
            reason: Some(response.error),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

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

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData>
    for UnifiedAuthenticationService
{
    //TODO: implement sessions flow
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken>
    for UnifiedAuthenticationService
{
}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for UnifiedAuthenticationService
{
}

impl
    ConnectorIntegration<
        AuthenticationConfirmation,
        UasConfirmationRequestData,
        UasAuthenticationResponseData,
    > for UnifiedAuthenticationService
{
    fn get_headers(
        &self,
        req: &UasAuthenticationConfirmationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &UasAuthenticationConfirmationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}confirmation", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &UasAuthenticationConfirmationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.transaction_amount,
            req.request.transaction_currency,
        )?;

        let connector_router_data =
            unified_authentication_service::UnifiedAuthenticationServiceRouterData::from((
                amount, req,
            ));
        let connector_req =
            unified_authentication_service::UnifiedAuthenticationServiceAuthenticateConfirmationRequest::try_from(
                &connector_router_data,
            )?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &UasAuthenticationConfirmationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::UasAuthenticationConfirmationType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::UasAuthenticationConfirmationType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::UasAuthenticationConfirmationType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &UasAuthenticationConfirmationRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<UasAuthenticationConfirmationRouterData, errors::ConnectorError> {
        let response: unified_authentication_service::UnifiedAuthenticationServiceAuthenticateConfirmationResponse =
            res.response
                .parse_struct("UnifiedAuthenticationService UnifiedAuthenticationServiceAuthenticateConfirmationResponse")
                .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl
    ConnectorIntegration<
        PreAuthenticate,
        UasPreAuthenticationRequestData,
        UasAuthenticationResponseData,
    > for UnifiedAuthenticationService
{
    fn get_headers(
        &self,
        req: &UasPreAuthenticationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &UasPreAuthenticationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}pre_authentication_processing",
            self.base_url(connectors)
        ))
    }

    fn get_request_body(
        &self,
        req: &UasPreAuthenticationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = if req.request.service_details.is_some() {
            let transaction_details = req.request.transaction_details.clone().ok_or(
                errors::ConnectorError::MissingRequiredField {
                    field_name: "transaction_details",
                },
            )?;
            let amount = utils::convert_amount(
                self.amount_converter,
                transaction_details
                    .amount
                    .ok_or(errors::ConnectorError::MissingRequiredField {
                        field_name: "amount",
                    })?,
                transaction_details.currency.ok_or(
                    errors::ConnectorError::MissingRequiredField {
                        field_name: "currency",
                    },
                )?,
            )?;

            let connector_router_data =
                unified_authentication_service::UnifiedAuthenticationServiceRouterData::from((
                    amount, req,
                ));
            unified_authentication_service::UnifiedAuthenticationServicePreAuthenticateRequest::try_from(
                        &connector_router_data,
                    )?
        } else {
            unified_authentication_service::UnifiedAuthenticationServicePreAuthenticateRequest::try_from(
                req,
            )?
        };
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &UasPreAuthenticationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::UasPreAuthenticationType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::UasPreAuthenticationType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::UasPreAuthenticationType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &UasPreAuthenticationRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<UasPreAuthenticationRouterData, errors::ConnectorError> {
        let response: unified_authentication_service::UnifiedAuthenticationServicePreAuthenticateResponse =
            res.response
                .parse_struct("UnifiedAuthenticationService UnifiedAuthenticationServicePreAuthenticateResponse")
                .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl
    ConnectorIntegration<
        PostAuthenticate,
        UasPostAuthenticationRequestData,
        UasAuthenticationResponseData,
    > for UnifiedAuthenticationService
{
    fn get_headers(
        &self,
        req: &UasPostAuthenticationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &UasPostAuthenticationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}post_authentication_sync",
            self.base_url(connectors)
        ))
    }

    fn get_request_body(
        &self,
        req: &UasPostAuthenticationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req =
            unified_authentication_service::UnifiedAuthenticationServicePostAuthenticateRequest::try_from(
                req,
            )?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &UasPostAuthenticationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::UasPostAuthenticationType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::UasPostAuthenticationType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::UasPostAuthenticationType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &UasPostAuthenticationRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<UasPostAuthenticationRouterData, errors::ConnectorError> {
        let response: unified_authentication_service::UnifiedAuthenticationServicePostAuthenticateResponse =
            res.response
                .parse_struct("UnifiedAuthenticationService UnifiedAuthenticationServicePostAuthenticateResponse")
                .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Authenticate, UasAuthenticationRequestData, UasAuthenticationResponseData>
    for UnifiedAuthenticationService
{
    fn get_headers(
        &self,
        req: &UasAuthenticationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &UasAuthenticationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}authentication_initiation",
            self.base_url(connectors)
        ))
    }

    fn get_request_body(
        &self,
        req: &UasAuthenticationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let transaction_details = req.request.transaction_details.clone();
        let amount = utils::convert_amount(
            self.amount_converter,
            transaction_details
                .amount
                .ok_or(errors::ConnectorError::MissingRequiredField {
                    field_name: "amount",
                })?,
            transaction_details
                .currency
                .ok_or(errors::ConnectorError::MissingRequiredField {
                    field_name: "currency",
                })?,
        )?;

        let connector_router_data =
            unified_authentication_service::UnifiedAuthenticationServiceRouterData::from((
                amount, req,
            ));
        let connector_req = unified_authentication_service::UnifiedAuthenticationServiceAuthenticateRequest::try_from(
                    &connector_router_data,
                )?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &UasAuthenticationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::UasAuthenticationType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::UasAuthenticationType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::UasAuthenticationType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &UasAuthenticationRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<UasAuthenticationRouterData, errors::ConnectorError> {
        let response: unified_authentication_service::UnifiedAuthenticationServiceAuthenticateResponse =
        res.response
            .parse_struct("UnifiedAuthenticationService UnifiedAuthenticationServiceAuthenticateResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData>
    for UnifiedAuthenticationService
{
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData>
    for UnifiedAuthenticationService
{
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData>
    for UnifiedAuthenticationService
{
}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData>
    for UnifiedAuthenticationService
{
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData>
    for UnifiedAuthenticationService
{
}

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

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

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

impl ConnectorSpecifications for UnifiedAuthenticationService {}


pub struct PaymentMethodCreated;


pub fn get_auth_event_metrics_info() -> Vec<NameDescription> {
    AuthEventMetrics::iter().map(Into::into).collect()
}


OpenAPI Block Path: components.schemas.XenditSplitSubMerchantData
{
  "type": "object",
  "description": "Fee information to be charged on the payment being collected for sub-merchant via xendit",
  "required": [
    "for_user_id"
  ],
  "properties": {
    "for_user_id": {
      "type": "string",
      "description": "The sub-account user-id that you want to make this transaction for."
    }
  },
  "additionalProperties": false
}


impl api::PaymentSession for Blackhawknetwork {}

impl api::PaymentSession for for Blackhawknetwork


pub async fn find_optional_by_merchant_id_key_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        key_id: &common_utils::id_type::ApiKeyId,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_one_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::key_id.eq(key_id.to_owned())),
        )
        .await
    }


pub struct GlobalpayAuthType {
    pub app_id: Secret<String>,
    pub key: Secret<String>,
}


pub fn get_mandate_type(
    mandate_data: Option<api_payments::MandateData>,
    off_session: Option<bool>,
    setup_future_usage: Option<enums::FutureUsage>,
    customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    token: Option<String>,
    payment_method: Option<enums::PaymentMethod>,
) -> CustomResult<Option<api::MandateTransactionType>, errors::ValidationError> {
    match (
        mandate_data.clone(),
        off_session,
        setup_future_usage,
        customer_acceptance.or(mandate_data.and_then(|m_data| m_data.customer_acceptance)),
        token,
        payment_method,
    ) {
        (Some(_), Some(_), Some(enums::FutureUsage::OffSession), Some(_), Some(_), _) => {
            Err(errors::ValidationError::InvalidValue {
                message: "Expected one out of recurring_details and mandate_data but got both"
                    .to_string(),
            }
            .into())
        }
        (_, _, Some(enums::FutureUsage::OffSession), Some(_), Some(_), _)
        | (_, _, Some(enums::FutureUsage::OffSession), Some(_), _, _)
        | (Some(_), _, Some(enums::FutureUsage::OffSession), _, _, _) => {
            Ok(Some(api::MandateTransactionType::NewMandateTransaction))
        }

        (_, _, Some(enums::FutureUsage::OffSession), _, Some(_), _)
        | (_, Some(_), _, _, _, _)
        | (_, _, Some(enums::FutureUsage::OffSession), _, _, Some(enums::PaymentMethod::Wallet)) => {
            Ok(Some(
                api::MandateTransactionType::RecurringMandateTransaction,
            ))
        }

        _ => Ok(None),
    }
}


impl ConnectorValidation for Billwerk {}

impl ConnectorValidation for for Billwerk


impl api::MandateSetup for Nuvei {}


pub async fn payments_dynamic_tax_calculation(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsDynamicTaxCalculationRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let flow = Flow::SessionUpdateTaxCalculation;
    let payment_id = path.into_inner();
    let payload = payment_types::PaymentsDynamicTaxCalculationRequest {
        payment_id,
        ..json_payload.into_inner()
    };
    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(error) => {
            logger::error!(
                ?error,
                "Failed to get headers in payments_connector_session"
            );
            HeaderPayload::default()
        }
    };
    tracing::Span::current().record("payment_id", payload.payment_id.get_string_repr());
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, payload, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::SdkSessionUpdate,
                payment_types::PaymentsDynamicTaxCalculationResponse,
                _,
                _,
                _,
                _,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentSessionUpdate,
                payload,
                api::AuthFlow::Client,
                payments::CallConnectorAction::Trigger,
                None,
                header_payload.clone(),
            )
        },
        &auth::PublishableKeyAuth,
        locking_action,
    ))
    .await
}


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


impl api::ConnectorAccessToken for Airwallex {}

impl api::ConnectorAccessToken for for Airwallex


impl api::revenue_recovery::BillingConnectorPaymentsSyncIntegration for Stripebilling {}

impl api::revenue_recovery::BillingConnectorPaymentsSyncIntegration for for Stripebilling


OpenAPI Block Path: components.schemas.RoutingRetrieveResponse
{
  "type": "object",
  "description": "Response of the retrieved routing configs for a merchant account",
  "properties": {
    "algorithm": {
      "allOf": [
        {
          "$ref": "#/components/schemas/MerchantRoutingAlgorithm"
        }
      ],
      "nullable": true
    }
  }
}


Documentation: docs/CODE_OF_CONDUCT.md
# Type: Doc File

# Code of Conduct

The hyperswitch project adheres to the
[Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct).
This describes the minimum behavior expected from all contributors.


pub async fn update_role(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<role_api::UpdateRoleRequest>,
    path: web::Path<String>,
) -> HttpResponse {
    let flow = Flow::UpdateRole;
    let role_id = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, user, req, _| role_core::update_role(state, user, req, &role_id),
        &auth::JWTAuth {
            permission: Permission::MerchantUserWrite,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl api::MandateSetup for Affirm {}


pub struct FiservRouterData<T> {
    pub amount: FloatMajorUnit,
    pub router_data: T,
}


File: crates/common_utils/src/pii.rs
 Public structs: 3

//! Personal Identifiable Information protection.

use std::{convert::AsRef, fmt, ops, str::FromStr};

use diesel::{
    backend::Backend,
    deserialize,
    deserialize::FromSql,
    prelude::*,
    serialize::{Output, ToSql},
    sql_types, AsExpression,
};
use error_stack::ResultExt;
use masking::{ExposeInterface, Secret, Strategy, WithType};
#[cfg(feature = "logs")]
use router_env::logger;
use serde::Deserialize;

use crate::{
    crypto::Encryptable,
    errors::{self, ValidationError},
    validation::{validate_email, validate_phone_number},
};

/// A string constant representing a redacted or masked value.
pub const REDACTED: &str = "Redacted";

/// Type alias for serde_json value which has Secret Information
pub type SecretSerdeValue = Secret<serde_json::Value>;

/// Strategy for masking a PhoneNumber
#[derive(Debug)]
pub enum PhoneNumberStrategy {}

/// Phone Number
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(try_from = "String")]
pub struct PhoneNumber(Secret<String, PhoneNumberStrategy>);

impl<T> Strategy<T> for PhoneNumberStrategy
where
    T: AsRef<str> + fmt::Debug,
{
    fn fmt(val: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let val_str: &str = val.as_ref();

        if let Some(val_str) = val_str.get(val_str.len() - 4..) {
            // masks everything but the last 4 digits
            write!(f, "{}{}", "*".repeat(val_str.len() - 4), val_str)
        } else {
            #[cfg(feature = "logs")]
            logger::error!("Invalid phone number: {val_str}");
            WithType::fmt(val, f)
        }
    }
}

impl FromStr for PhoneNumber {
    type Err = error_stack::Report<ValidationError>;
    fn from_str(phone_number: &str) -> Result<Self, Self::Err> {
        validate_phone_number(phone_number)?;
        let secret = Secret::<String, PhoneNumberStrategy>::new(phone_number.to_string());
        Ok(Self(secret))
    }
}

impl TryFrom<String> for PhoneNumber {
    type Error = error_stack::Report<errors::ParsingError>;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        Self::from_str(&value).change_context(errors::ParsingError::PhoneNumberParsingError)
    }
}

impl ops::Deref for PhoneNumber {
    type Target = Secret<String, PhoneNumberStrategy>;

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

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

impl<DB> Queryable<sql_types::Text, DB> for PhoneNumber
where
    DB: Backend,
    Self: FromSql<sql_types::Text, DB>,
{
    type Row = Self;

    fn build(row: Self::Row) -> deserialize::Result<Self> {
        Ok(row)
    }
}

impl<DB> FromSql<sql_types::Text, DB> for PhoneNumber
where
    DB: Backend,
    String: FromSql<sql_types::Text, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
        let val = String::from_sql(bytes)?;
        Ok(Self::from_str(val.as_str())?)
    }
}

impl<DB> ToSql<sql_types::Text, DB> for PhoneNumber
where
    DB: Backend,
    String: ToSql<sql_types::Text, DB>,
{
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
        self.0.to_sql(out)
    }
}

/*
/// Phone number
#[derive(Debug)]
pub struct PhoneNumber;

impl<T> Strategy<T> for PhoneNumber
where
    T: AsRef<str>,
{
    fn fmt(val: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let val_str: &str = val.as_ref();

        if val_str.len() < 10 || val_str.len() > 12 {
            return WithType::fmt(val, f);
        }

        write!(
            f,
            "{}{}{}",
            &val_str[..2],
            "*".repeat(val_str.len() - 5),
            &val_str[(val_str.len() - 3)..]
        )
    }
}
*/

/// Strategy for Encryption
#[derive(Debug)]
pub enum EncryptionStrategy {}

impl<T> Strategy<T> for EncryptionStrategy
where
    T: AsRef<[u8]>,
{
    fn fmt(value: &T, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(
            fmt,
            "*** Encrypted data of length {} bytes ***",
            value.as_ref().len()
        )
    }
}

/// Client secret
#[derive(Debug)]
pub enum ClientSecret {}

impl<T> Strategy<T> for ClientSecret
where
    T: AsRef<str>,
{
    fn fmt(val: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let val_str: &str = val.as_ref();

        let client_secret_segments: Vec<&str> = val_str.split('_').collect();

        if client_secret_segments.len() != 4
            || !client_secret_segments.contains(&"pay")
            || !client_secret_segments.contains(&"secret")
        {
            return WithType::fmt(val, f);
        }

        if let Some((client_secret_segments_0, client_secret_segments_1)) = client_secret_segments
            .first()
            .zip(client_secret_segments.get(1))
        {
            write!(
                f,
                "{}_{}_{}",
                client_secret_segments_0,
                client_secret_segments_1,
                "*".repeat(
                    val_str.len()
                        - (client_secret_segments_0.len() + client_secret_segments_1.len() + 2)
                )
            )
        } else {
            #[cfg(feature = "logs")]
            logger::error!("Invalid client secret: {val_str}");
            WithType::fmt(val, f)
        }
    }
}

/// Strategy for masking Email
#[derive(Debug, Copy, Clone, Deserialize)]
pub enum EmailStrategy {}

impl<T> Strategy<T> for EmailStrategy
where
    T: AsRef<str> + fmt::Debug,
{
    fn fmt(val: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let val_str: &str = val.as_ref();
        match val_str.split_once('@') {
            Some((a, b)) => write!(f, "{}@{}", "*".repeat(a.len()), b),
            None => WithType::fmt(val, f),
        }
    }
}
/// Email address
#[derive(
    serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, Default, AsExpression,
)]
#[diesel(sql_type = sql_types::Text)]
#[serde(try_from = "String")]
pub struct Email(Secret<String, EmailStrategy>);

impl From<Encryptable<Secret<String, EmailStrategy>>> for Email {
    fn from(item: Encryptable<Secret<String, EmailStrategy>>) -> Self {
        Self(item.into_inner())
    }
}

impl ExposeInterface<Secret<String, EmailStrategy>> for Email {
    fn expose(self) -> Secret<String, EmailStrategy> {
        self.0
    }
}

impl TryFrom<String> for Email {
    type Error = error_stack::Report<errors::ParsingError>;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        Self::from_str(&value).change_context(errors::ParsingError::EmailParsingError)
    }
}

impl ops::Deref for Email {
    type Target = Secret<String, EmailStrategy>;

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

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

impl<DB> Queryable<sql_types::Text, DB> for Email
where
    DB: Backend,
    Self: FromSql<sql_types::Text, DB>,
{
    type Row = Self;

    fn build(row: Self::Row) -> deserialize::Result<Self> {
        Ok(row)
    }
}

impl<DB> FromSql<sql_types::Text, DB> for Email
where
    DB: Backend,
    String: FromSql<sql_types::Text, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
        let val = String::from_sql(bytes)?;
        Ok(Self::from_str(val.as_str())?)
    }
}

impl<DB> ToSql<sql_types::Text, DB> for Email
where
    DB: Backend,
    String: ToSql<sql_types::Text, DB>,
{
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result {
        self.0.to_sql(out)
    }
}

impl FromStr for Email {
    type Err = error_stack::Report<ValidationError>;
    fn from_str(email: &str) -> Result<Self, Self::Err> {
        if email.eq(REDACTED) {
            return Ok(Self(Secret::new(email.to_string())));
        }
        match validate_email(email) {
            Ok(_) => {
                let secret = Secret::<String, EmailStrategy>::new(email.to_string());
                Ok(Self(secret))
            }
            Err(_) => Err(ValidationError::InvalidValue {
                message: "Invalid email address format".into(),
            }
            .into()),
        }
    }
}

/// IP address
#[derive(Debug)]
pub enum IpAddress {}

impl<T> Strategy<T> for IpAddress
where
    T: AsRef<str>,
{
    fn fmt(val: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let val_str: &str = val.as_ref();
        let segments: Vec<&str> = val_str.split('.').collect();

        if segments.len() != 4 {
            return WithType::fmt(val, f);
        }

        for seg in segments.iter() {
            if seg.is_empty() || seg.len() > 3 {
                return WithType::fmt(val, f);
            }
        }

        if let Some(segments) = segments.first() {
            write!(f, "{segments}.**.**.**")
        } else {
            #[cfg(feature = "logs")]
            logger::error!("Invalid IP address: {val_str}");
            WithType::fmt(val, f)
        }
    }
}

/// Strategy for masking UPI VPA's
#[derive(Debug)]
pub enum UpiVpaMaskingStrategy {}

impl<T> Strategy<T> for UpiVpaMaskingStrategy
where
    T: AsRef<str> + fmt::Debug,
{
    fn fmt(val: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let vpa_str: &str = val.as_ref();
        if let Some((user_identifier, bank_or_psp)) = vpa_str.split_once('@') {
            let masked_user_identifier = "*".repeat(user_identifier.len());
            write!(f, "{masked_user_identifier}@{bank_or_psp}")
        } else {
            WithType::fmt(val, f)
        }
    }
}

#[cfg(test)]
mod pii_masking_strategy_tests {
    use std::str::FromStr;

    use masking::{ExposeInterface, Secret};

    use super::{ClientSecret, Email, IpAddress, UpiVpaMaskingStrategy};
    use crate::pii::{EmailStrategy, REDACTED};

    /*
    #[test]
    fn test_valid_phone_number_masking() {
        let secret: Secret<String, PhoneNumber> = Secret::new("9123456789".to_string());
        assert_eq!("99*****299", format!("{}", secret));
    }

    #[test]
    fn test_invalid_phone_number_masking() {
        let secret: Secret<String, PhoneNumber> = Secret::new("99229922".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{}", secret));

        let secret: Secret<String, PhoneNumber> = Secret::new("9922992299229922".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{}", secret));
    }
    */

    #[test]
    fn test_valid_email_masking() {
        let secret: Secret<String, EmailStrategy> = Secret::new("example@test.com".to_string());
        assert_eq!("*******@test.com", format!("{secret:?}"));

        let secret: Secret<String, EmailStrategy> = Secret::new("username@gmail.com".to_string());
        assert_eq!("********@gmail.com", format!("{secret:?}"));
    }

    #[test]
    fn test_invalid_email_masking() {
        let secret: Secret<String, EmailStrategy> = Secret::new("myemailgmail.com".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{secret:?}"));

        let secret: Secret<String, EmailStrategy> = Secret::new("myemail$gmail.com".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{secret:?}"));
    }

    #[test]
    fn test_valid_newtype_email() {
        let email_check = Email::from_str("example@abc.com");
        assert!(email_check.is_ok());
    }

    #[test]
    fn test_invalid_newtype_email() {
        let email_check = Email::from_str("example@abc@com");
        assert!(email_check.is_err());
    }

    #[test]
    fn test_redacted_email() {
        let email_result = Email::from_str(REDACTED);
        assert!(email_result.is_ok());
        if let Ok(email) = email_result {
            let secret_value = email.0.expose();
            assert_eq!(secret_value.as_str(), REDACTED);
        }
    }

    #[test]
    fn test_valid_ip_addr_masking() {
        let secret: Secret<String, IpAddress> = Secret::new("123.23.1.78".to_string());
        assert_eq!("123.**.**.**", format!("{secret:?}"));
    }

    #[test]
    fn test_invalid_ip_addr_masking() {
        let secret: Secret<String, IpAddress> = Secret::new("123.4.56".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{secret:?}"));

        let secret: Secret<String, IpAddress> = Secret::new("123.4567.12.4".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{secret:?}"));

        let secret: Secret<String, IpAddress> = Secret::new("123..4.56".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{secret:?}"));
    }

    #[test]
    fn test_valid_client_secret_masking() {
        let secret: Secret<String, ClientSecret> =
            Secret::new("pay_uszFB2QGe9MmLY65ojhT_secret_tLjTz9tAQxUVEFqfmOIP".to_string());
        assert_eq!(
            "pay_uszFB2QGe9MmLY65ojhT_***************************",
            format!("{secret:?}")
        );
    }

    #[test]
    fn test_invalid_client_secret_masking() {
        let secret: Secret<String, IpAddress> =
            Secret::new("pay_uszFB2QGe9MmLY65ojhT_secret".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{secret:?}"));
    }

    #[test]
    fn test_valid_phone_number_default_masking() {
        let secret: Secret<String> = Secret::new("+40712345678".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{secret:?}"));
    }

    #[test]
    fn test_valid_upi_vpa_masking() {
        let secret: Secret<String, UpiVpaMaskingStrategy> = Secret::new("my_name@upi".to_string());
        assert_eq!("*******@upi", format!("{secret:?}"));
    }

    #[test]
    fn test_invalid_upi_vpa_masking() {
        let secret: Secret<String, UpiVpaMaskingStrategy> = Secret::new("my_name_upi".to_string());
        assert_eq!("*** alloc::string::String ***", format!("{secret:?}"));
    }
}


impl FraudCheckTransaction for Signifyd {}

impl FraudCheckTransaction for for Signifyd


pub struct PaymeVoidRequest {
    sale_currency: enums::Currency,
    payme_sale_id: String,
    seller_payme_id: Secret<String>,
    language: String,
}


impl super::behaviour::Conversion for Profile {
    type DstType = diesel_models::business_profile::Profile;
    type NewDstType = diesel_models::business_profile::ProfileNew;

    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(diesel_models::business_profile::Profile {
            profile_id: self.profile_id.clone(),
            id: Some(self.profile_id),
            merchant_id: self.merchant_id,
            profile_name: self.profile_name,
            created_at: self.created_at,
            modified_at: self.modified_at,
            return_url: self.return_url,
            enable_payment_response_hash: self.enable_payment_response_hash,
            payment_response_hash_key: self.payment_response_hash_key,
            redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post,
            webhook_details: self.webhook_details,
            metadata: self.metadata,
            routing_algorithm: self.routing_algorithm,
            intent_fulfillment_time: self.intent_fulfillment_time,
            frm_routing_algorithm: self.frm_routing_algorithm,
            payout_routing_algorithm: self.payout_routing_algorithm,
            is_recon_enabled: self.is_recon_enabled,
            applepay_verified_domains: self.applepay_verified_domains,
            payment_link_config: self.payment_link_config,
            session_expiry: self.session_expiry,
            authentication_connector_details: self.authentication_connector_details,
            payout_link_config: self.payout_link_config,
            is_extended_card_info_enabled: self.is_extended_card_info_enabled,
            extended_card_info_config: self.extended_card_info_config,
            is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled,
            use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing,
            collect_shipping_details_from_wallet_connector: self
                .collect_shipping_details_from_wallet_connector,
            collect_billing_details_from_wallet_connector: self
                .collect_billing_details_from_wallet_connector,
            outgoing_webhook_custom_http_headers: self
                .outgoing_webhook_custom_http_headers
                .map(Encryption::from),
            always_collect_billing_details_from_wallet_connector: self
                .always_collect_billing_details_from_wallet_connector,
            always_collect_shipping_details_from_wallet_connector: self
                .always_collect_shipping_details_from_wallet_connector,
            tax_connector_id: self.tax_connector_id,
            is_tax_connector_enabled: Some(self.is_tax_connector_enabled),
            version: self.version,
            dynamic_routing_algorithm: self.dynamic_routing_algorithm,
            is_network_tokenization_enabled: self.is_network_tokenization_enabled,
            is_auto_retries_enabled: Some(self.is_auto_retries_enabled),
            max_auto_retries_enabled: self.max_auto_retries_enabled,
            always_request_extended_authorization: self.always_request_extended_authorization,
            is_click_to_pay_enabled: self.is_click_to_pay_enabled,
            authentication_product_ids: self.authentication_product_ids,
            card_testing_guard_config: self.card_testing_guard_config,
            card_testing_secret_key: self.card_testing_secret_key.map(|name| name.into()),
            is_clear_pan_retries_enabled: self.is_clear_pan_retries_enabled,
            force_3ds_challenge: Some(self.force_3ds_challenge),
            is_debit_routing_enabled: self.is_debit_routing_enabled,
            merchant_business_country: self.merchant_business_country,
            is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
            is_pre_network_tokenization_enabled: Some(self.is_pre_network_tokenization_enabled),
            three_ds_decision_rule_algorithm: self.three_ds_decision_rule_algorithm,
            acquirer_config_map: self.acquirer_config_map,
            merchant_category_code: self.merchant_category_code,
            merchant_country_code: self.merchant_country_code,
            dispute_polling_interval: self.dispute_polling_interval,
            is_manual_retry_enabled: self.is_manual_retry_enabled,
        })
    }

    async fn convert_back(
        state: &keymanager::KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        async {
            Ok::<Self, error_stack::Report<common_utils::errors::CryptoError>>(Self {
                profile_id: item.profile_id,
                merchant_id: item.merchant_id,
                profile_name: item.profile_name,
                created_at: item.created_at,
                modified_at: item.modified_at,
                return_url: item.return_url,
                enable_payment_response_hash: item.enable_payment_response_hash,
                payment_response_hash_key: item.payment_response_hash_key,
                redirect_to_merchant_with_http_post: item.redirect_to_merchant_with_http_post,
                webhook_details: item.webhook_details,
                metadata: item.metadata,
                routing_algorithm: item.routing_algorithm,
                intent_fulfillment_time: item.intent_fulfillment_time,
                frm_routing_algorithm: item.frm_routing_algorithm,
                payout_routing_algorithm: item.payout_routing_algorithm,
                is_recon_enabled: item.is_recon_enabled,
                applepay_verified_domains: item.applepay_verified_domains,
                payment_link_config: item.payment_link_config,
                session_expiry: item.session_expiry,
                authentication_connector_details: item.authentication_connector_details,
                payout_link_config: item.payout_link_config,
                is_extended_card_info_enabled: item.is_extended_card_info_enabled,
                extended_card_info_config: item.extended_card_info_config,
                is_connector_agnostic_mit_enabled: item.is_connector_agnostic_mit_enabled,
                use_billing_as_payment_method_billing: item.use_billing_as_payment_method_billing,
                collect_shipping_details_from_wallet_connector: item
                    .collect_shipping_details_from_wallet_connector,
                collect_billing_details_from_wallet_connector: item
                    .collect_billing_details_from_wallet_connector,
                always_collect_billing_details_from_wallet_connector: item
                    .always_collect_billing_details_from_wallet_connector,
                always_collect_shipping_details_from_wallet_connector: item
                    .always_collect_shipping_details_from_wallet_connector,
                outgoing_webhook_custom_http_headers: item
                    .outgoing_webhook_custom_http_headers
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                tax_connector_id: item.tax_connector_id,
                is_tax_connector_enabled: item.is_tax_connector_enabled.unwrap_or(false),
                version: item.version,
                dynamic_routing_algorithm: item.dynamic_routing_algorithm,
                is_network_tokenization_enabled: item.is_network_tokenization_enabled,
                is_auto_retries_enabled: item.is_auto_retries_enabled.unwrap_or(false),
                max_auto_retries_enabled: item.max_auto_retries_enabled,
                always_request_extended_authorization: item.always_request_extended_authorization,
                is_click_to_pay_enabled: item.is_click_to_pay_enabled,
                authentication_product_ids: item.authentication_product_ids,
                card_testing_guard_config: item.card_testing_guard_config,
                card_testing_secret_key: item
                    .card_testing_secret_key
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                is_clear_pan_retries_enabled: item.is_clear_pan_retries_enabled,
                force_3ds_challenge: item.force_3ds_challenge.unwrap_or_default(),
                is_debit_routing_enabled: item.is_debit_routing_enabled,
                merchant_business_country: item.merchant_business_country,
                is_iframe_redirection_enabled: item.is_iframe_redirection_enabled,
                is_pre_network_tokenization_enabled: item
                    .is_pre_network_tokenization_enabled
                    .unwrap_or(false),
                three_ds_decision_rule_algorithm: item.three_ds_decision_rule_algorithm,
                acquirer_config_map: item.acquirer_config_map,
                merchant_category_code: item.merchant_category_code,
                merchant_country_code: item.merchant_country_code,
                dispute_polling_interval: item.dispute_polling_interval,
                is_manual_retry_enabled: item.is_manual_retry_enabled,
            })
        }
        .await
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting business profile data".to_string(),
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        Ok(diesel_models::business_profile::ProfileNew {
            profile_id: self.profile_id.clone(),
            id: Some(self.profile_id),
            merchant_id: self.merchant_id,
            profile_name: self.profile_name,
            created_at: self.created_at,
            modified_at: self.modified_at,
            return_url: self.return_url,
            enable_payment_response_hash: self.enable_payment_response_hash,
            payment_response_hash_key: self.payment_response_hash_key,
            redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post,
            webhook_details: self.webhook_details,
            metadata: self.metadata,
            routing_algorithm: self.routing_algorithm,
            intent_fulfillment_time: self.intent_fulfillment_time,
            frm_routing_algorithm: self.frm_routing_algorithm,
            payout_routing_algorithm: self.payout_routing_algorithm,
            is_recon_enabled: self.is_recon_enabled,
            applepay_verified_domains: self.applepay_verified_domains,
            payment_link_config: self.payment_link_config,
            session_expiry: self.session_expiry,
            authentication_connector_details: self.authentication_connector_details,
            payout_link_config: self.payout_link_config,
            is_extended_card_info_enabled: self.is_extended_card_info_enabled,
            extended_card_info_config: self.extended_card_info_config,
            is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled,
            use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing,
            collect_shipping_details_from_wallet_connector: self
                .collect_shipping_details_from_wallet_connector,
            collect_billing_details_from_wallet_connector: self
                .collect_billing_details_from_wallet_connector,
            outgoing_webhook_custom_http_headers: self
                .outgoing_webhook_custom_http_headers
                .map(Encryption::from),
            always_collect_billing_details_from_wallet_connector: self
                .always_collect_billing_details_from_wallet_connector,
            always_collect_shipping_details_from_wallet_connector: self
                .always_collect_shipping_details_from_wallet_connector,
            tax_connector_id: self.tax_connector_id,
            is_tax_connector_enabled: Some(self.is_tax_connector_enabled),
            version: self.version,
            is_network_tokenization_enabled: self.is_network_tokenization_enabled,
            is_auto_retries_enabled: Some(self.is_auto_retries_enabled),
            max_auto_retries_enabled: self.max_auto_retries_enabled,
            is_click_to_pay_enabled: self.is_click_to_pay_enabled,
            authentication_product_ids: self.authentication_product_ids,
            card_testing_guard_config: self.card_testing_guard_config,
            card_testing_secret_key: self.card_testing_secret_key.map(Encryption::from),
            is_clear_pan_retries_enabled: self.is_clear_pan_retries_enabled,
            force_3ds_challenge: Some(self.force_3ds_challenge),
            is_debit_routing_enabled: self.is_debit_routing_enabled,
            merchant_business_country: self.merchant_business_country,
            is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
            is_pre_network_tokenization_enabled: Some(self.is_pre_network_tokenization_enabled),
            merchant_category_code: self.merchant_category_code,
            merchant_country_code: self.merchant_country_code,
            dispute_polling_interval: self.dispute_polling_interval,
            is_manual_retry_enabled: self.is_manual_retry_enabled,
        })
    }
}

impl super::behaviour::Conversion for for Profile