File: crates/hyperswitch_connectors/src/connectors/juspaythreedsserver/transformers.rs
 Public structs: 8

use common_enums::enums;
use common_utils::types::StringMinorUnit;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::PaymentsAuthorizeRequestData,
};

//TODO: Fill the struct with respective fields
pub struct JuspaythreedsserverRouterData<T> {
    pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

impl<T> From<(StringMinorUnit, T)> for JuspaythreedsserverRouterData<T> {
    fn from((amount, item): (StringMinorUnit, T)) -> Self {
        //Todo :  use utils to convert the amount to the type of amount that a connector accepts
        Self {
            amount,
            router_data: item,
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, PartialEq)]
pub struct JuspaythreedsserverPaymentsRequest {
    amount: StringMinorUnit,
    card: JuspaythreedsserverCard,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct JuspaythreedsserverCard {
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
    complete: bool,
}

impl TryFrom<&JuspaythreedsserverRouterData<&PaymentsAuthorizeRouterData>>
    for JuspaythreedsserverPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &JuspaythreedsserverRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = JuspaythreedsserverCard {
                    number: req_card.card_number,
                    expiry_month: req_card.card_exp_month,
                    expiry_year: req_card.card_exp_year,
                    cvc: req_card.card_cvc,
                    complete: item.router_data.request.is_auto_capture()?,
                };
                Ok(Self {
                    amount: item.amount.clone(),
                    card,
                })
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

//TODO: Fill the struct with respective fields
// Auth Struct
pub struct JuspaythreedsserverAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for JuspaythreedsserverAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                api_key: api_key.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}
// PaymentsResponse
//TODO: Append the remaining status flags
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum JuspaythreedsserverPaymentStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<JuspaythreedsserverPaymentStatus> for common_enums::AttemptStatus {
    fn from(item: JuspaythreedsserverPaymentStatus) -> Self {
        match item {
            JuspaythreedsserverPaymentStatus::Succeeded => Self::Charged,
            JuspaythreedsserverPaymentStatus::Failed => Self::Failure,
            JuspaythreedsserverPaymentStatus::Processing => Self::Authorizing,
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct JuspaythreedsserverPaymentsResponse {
    status: JuspaythreedsserverPaymentStatus,
    id: String,
}

impl<F, T>
    TryFrom<ResponseRouterData<F, JuspaythreedsserverPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, JuspaythreedsserverPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: common_enums::AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
// REFUND :
// Type definition for RefundRequest
#[derive(Default, Debug, Serialize)]
pub struct JuspaythreedsserverRefundRequest {
    pub amount: StringMinorUnit,
}

impl<F> TryFrom<&JuspaythreedsserverRouterData<&RefundsRouterData<F>>>
    for JuspaythreedsserverRefundRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &JuspaythreedsserverRouterData<&RefundsRouterData<F>>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: item.amount.to_owned(),
        })
    }
}

// Type definition for Refund Response

#[allow(dead_code)]
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
pub enum RefundStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<RefundStatus> for enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Succeeded => Self::Success,
            RefundStatus::Failed => Self::Failure,
            RefundStatus::Processing => Self::Pending,
            //TODO: Review mapping
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse {
    id: String,
    status: RefundStatus,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct JuspaythreedsserverErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
}


pub struct NuveiPaymentRequestData {
    pub amount: StringMajorUnit,
    pub currency: enums::Currency,
    pub related_transaction_id: Option<String>,
    pub client_request_id: String,
    pub connector_auth_type: ConnectorAuthType,
    pub session_token: Secret<String>,
    pub capture_method: Option<CaptureMethod>,
}


pub async fn update_payment_method_connector_mandate_details(
    state: &routes::SessionState,
    key_store: &domain::MerchantKeyStore,
    db: &dyn db::StorageInterface,
    pm: domain::PaymentMethod,
    connector_mandate_details: Option<CommonMandateReference>,
    storage_scheme: MerchantStorageScheme,
) -> errors::CustomResult<(), errors::VaultError> {
    let connector_mandate_details_value = connector_mandate_details
        .map(|common_mandate| {
            common_mandate.get_mandate_details_value().map_err(|err| {
                router_env::logger::error!("Failed to get get_mandate_details_value : {:?}", err);
                errors::VaultError::UpdateInPaymentMethodDataTableFailed
            })
        })
        .transpose()?;

    let pm_update = payment_method::PaymentMethodUpdate::ConnectorMandateDetailsUpdate {
        connector_mandate_details: connector_mandate_details_value,
    };

    db.update_payment_method(&(state.into()), key_store, pm, pm_update, storage_scheme)
        .await
        .change_context(errors::VaultError::UpdateInPaymentMethodDataTableFailed)?;
    Ok(())
}


impl api::PaymentsPreProcessing for Redsys {}

impl api::PaymentsPreProcessing for for Redsys


pub struct MbWayInfo {}


File: crates/analytics/src/outgoing_webhook_event/core.rs
 Public functions: 1

use api_models::analytics::outgoing_webhook_event::OutgoingWebhookLogsRequest;
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;

use super::events::{get_outgoing_webhook_event, OutgoingWebhookLogsResult};
use crate::{errors::AnalyticsResult, types::FiltersError, AnalyticsProvider};

pub async fn outgoing_webhook_events_core(
    pool: &AnalyticsProvider,
    req: OutgoingWebhookLogsRequest,
    merchant_id: &common_utils::id_type::MerchantId,
) -> AnalyticsResult<Vec<OutgoingWebhookLogsResult>> {
    let data = match pool {
        AnalyticsProvider::Sqlx(_) => Err(FiltersError::NotImplemented(
            "Outgoing Webhook Events Logs not implemented for SQLX",
        ))
        .attach_printable("SQL Analytics is not implemented for Outgoing Webhook Events"),
        AnalyticsProvider::Clickhouse(ckh_pool)
        | AnalyticsProvider::CombinedSqlx(_, ckh_pool)
        | AnalyticsProvider::CombinedCkh(_, ckh_pool) => {
            get_outgoing_webhook_event(merchant_id, req, ckh_pool).await
        }
    }
    .switch()?;
    Ok(data)
}


pub fn knowledge(ts: TokenStream) -> TokenStream {
    match inner::knowledge_inner(ts.into()) {
        Ok(ts) => ts.into(),
        Err(e) => e.into_compile_error().into(),
    }
}


impl api::PaymentToken for Bluecode {}


pub fn get_optional_connector_metadata(
        &self,
    ) -> CustomResult<
        Option<api_models::payments::ConnectorMetadata>,
        common_utils::errors::ParsingError,
    > {
        self.connector_metadata
            .clone()
            .map(|cm| {
                cm.parse_value::<api_models::payments::ConnectorMetadata>("ConnectorMetadata")
            })
            .transpose()
    }


pub struct CaptureBuyerRequest {
    seller_payme_id: Secret<String>,
    #[serde(flatten)]
    card: PaymeCard,
}


pub struct VoltRefundsWebhookBodyEventType {
    pub status: VoltWebhookRefundsStatus,
}


pub struct ResultInfo {
    pub result_code: i64,
    pub additional_info: Option<String>,
    pub correlation_id: Option<String>,
}


impl api::Payment for Stax {}


impl VaultOperation {
    pub fn get_updated_vault_data(
        existing_vault_data: Option<&Self>,
        payment_method_data: &payment_method_data::PaymentMethodData,
    ) -> Option<Self> {
        match (existing_vault_data, payment_method_data) {
            (None, payment_method_data::PaymentMethodData::Card(card)) => {
                Some(Self::ExistingVaultData(VaultData::Card(card.clone())))
            }
            (None, payment_method_data::PaymentMethodData::NetworkToken(nt_data)) => Some(
                Self::ExistingVaultData(VaultData::NetworkToken(nt_data.clone())),
            ),
            (Some(Self::ExistingVaultData(vault_data)), payment_method_data) => {
                match (vault_data, payment_method_data) {
                    (
                        VaultData::Card(card),
                        payment_method_data::PaymentMethodData::NetworkToken(nt_data),
                    ) => Some(Self::ExistingVaultData(VaultData::CardAndNetworkToken(
                        Box::new(CardAndNetworkTokenData {
                            card_data: card.clone(),
                            network_token_data: nt_data.clone(),
                        }),
                    ))),
                    (
                        VaultData::NetworkToken(nt_data),
                        payment_method_data::PaymentMethodData::Card(card),
                    ) => Some(Self::ExistingVaultData(VaultData::CardAndNetworkToken(
                        Box::new(CardAndNetworkTokenData {
                            card_data: card.clone(),
                            network_token_data: nt_data.clone(),
                        }),
                    ))),
                    _ => Some(Self::ExistingVaultData(vault_data.clone())),
                }
            }
            //payment_method_data is not card or network token
            _ => None,
        }
    }
}


pub struct Reject;


pub struct DummyConnectorPaymentRetrieveRequest {
    pub payment_id: String,
}


pub fn payment_method_session_update_saved_payment_method() {}


pub async fn switch_merchant_for_user_in_org(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<user_api::SwitchMerchantRequest>,
) -> HttpResponse {
    let flow = Flow::SwitchMerchantV2;
    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &http_req,
        json_payload.into_inner(),
        |state, user, req, _| user_core::switch_merchant_for_user_in_org(state, req, user),
        &auth::DashboardNoPermissionAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl api::PaymentCapture for Phonepe {}

impl api::PaymentCapture for for Phonepe


impl api::MandateSetup for Novalnet {}


pub async fn find_by_user_id_tenant_id_org_id_merchant_id_profile_id(
        conn: &PgPooledConn,
        user_id: String,
        tenant_id: id_type::TenantId,
        org_id: id_type::OrganizationId,
        merchant_id: id_type::MerchantId,
        profile_id: id_type::ProfileId,
        version: UserRoleVersion,
    ) -> StorageResult<Self> {
        let check_lineage = Self::check_user_in_lineage(
            tenant_id,
            Some(org_id),
            Some(merchant_id),
            Some(profile_id),
        );

        let predicate = dsl::user_id
            .eq(user_id)
            .and(check_lineage)
            .and(dsl::version.eq(version));

        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(conn, predicate).await
    }


pub async fn payments_link_list(
    state: web::Data<AppState>,
    req: actix_web::HttpRequest,
    payload: web::Query<api_models::payments::PaymentLinkListConstraints>,
) -> impl Responder {
    let flow = Flow::PaymentLinkList;
    let payload = payload.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, payload, _| {
            list_payment_link(state, auth.merchant_account, payload)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn generic_delete<T, P>(conn: &PgPooledConn, predicate: P) -> StorageResult<bool>
where
    T: FilterDsl<P> + HasTable<Table = T> + Table + 'static,
    Filter<T, P>: IntoUpdateTarget,
    DeleteStatement<
        <Filter<T, P> as HasTable>::Table,
        <Filter<T, P> as IntoUpdateTarget>::WhereClause,
    >: AsQuery + QueryFragment<Pg> + QueryId + Send + 'static,
{
    let query = diesel::delete(<T as HasTable>::table().filter(predicate));
    logger::debug!(query = %debug_query::<Pg, _>(&query).to_string());

    track_database_call::<T, _, _>(query.execute_async(conn), DatabaseOperation::Delete)
        .await
        .change_context(errors::DatabaseError::Others)
        .attach_printable("Error while deleting")
        .and_then(|result| match result {
            n if n > 0 => {
                logger::debug!("{n} records deleted");
                Ok(true)
            }
            0 => {
                Err(report!(errors::DatabaseError::NotFound).attach_printable("No records deleted"))
            }
            _ => Ok(true), // n is usize, rustc requires this for exhaustive check
        })
}


pub struct PlaidLinkTokenResponse {
    link_token: String,
}


pub struct PaymentMethodsEnabled {
    pub payment_method: api_models::enums::PaymentMethod,
    pub payment_method_types: Option<Vec<payment_methods::RequestPaymentMethodTypes>>,
}


pub fn flat_struct(&self) -> std::collections::HashMap<String, String> {
                use serde_json::Value;
                use std::collections::HashMap;

                fn flatten_value(
                    value: &Value,
                    prefix: &str,
                    result: &mut HashMap<String, String>
                ) {
                    match value {
                        Value::Object(map) => {
                            for (key, val) in map {
                                let new_key = if prefix.is_empty() {
                                    key.to_string()
                                } else {
                                    format!("{}.{}", prefix, key)
                                };
                                flatten_value(val, &new_key, result);
                            }
                        }
                        Value::String(s) => {
                            result.insert(prefix.to_string(), s.clone());
                        }
                        Value::Number(n) => {
                            result.insert(prefix.to_string(), n.to_string());
                        }
                        Value::Bool(b) => {
                            result.insert(prefix.to_string(), b.to_string());
                        }
                        _ => {}
                    }
                }

                let mut result = HashMap::new();
                let value = serde_json::to_value(self).unwrap();
                flatten_value(&value, "", &mut result);
                result
            }


File: crates/hyperswitch_connectors/src/connectors/worldpay/response.rs
 Public functions: 5
 Public structs: 31

use error_stack::ResultExt;
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};
use url::Url;

use super::requests::*;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WorldpayPaymentsResponse {
    pub outcome: PaymentOutcome,
    pub transaction_reference: Option<String>,
    #[serde(flatten)]
    pub other_fields: Option<WorldpayPaymentResponseFields>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum WorldpayPaymentResponseFields {
    RefusedResponse(RefusedResponse),
    DDCResponse(DDCResponse),
    ThreeDsChallenged(ThreeDsChallengedResponse),
    FraudHighRisk(FraudHighRiskResponse),
    AuthorizedResponse(Box<AuthorizedResponse>),
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthorizedResponse {
    pub payment_instrument: PaymentsResPaymentInstrument,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub issuer: Option<Issuer>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scheme: Option<PaymentsResponseScheme>,
    #[serde(rename = "_links", skip_serializing_if = "Option::is_none")]
    pub links: Option<SelfLink>,
    #[serde(rename = "_actions")]
    pub actions: Option<ActionLinks>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    pub risk_factors: Option<Vec<RiskFactorsInner>>,
    pub fraud: Option<Fraud>,
    /// Mandate's token
    pub token: Option<MandateToken>,
    /// Network transaction ID
    pub scheme_reference: Option<Secret<String>>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MandateToken {
    pub href: Secret<String>,
    pub token_id: String,
    pub token_expiry_date_time: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct FraudHighRiskResponse {
    pub score: f32,
    pub reason: Vec<String>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RefusedResponse {
    pub refusal_description: String,
    // Access Worldpay returns a raw response code in the refusalCode field (if enabled) containing the unmodified response code received either directly from the card scheme for Worldpay-acquired transactions, or from third party acquirers.
    pub refusal_code: String,
    pub risk_factors: Option<Vec<RiskFactorsInner>>,
    pub fraud: Option<Fraud>,
    #[serde(rename = "threeDS")]
    pub three_ds: Option<ThreeDsResponse>,
    pub advice: Option<Advice>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Advice {
    pub code: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreeDsResponse {
    pub outcome: String,
    pub issuer_response: IssuerResponse,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreeDsChallengedResponse {
    pub authentication: AuthenticationResponse,
    pub challenge: ThreeDsChallenge,
    #[serde(rename = "_actions")]
    pub actions: CompleteThreeDsActionLink,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct AuthenticationResponse {
    pub version: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ThreeDsChallenge {
    pub reference: String,
    pub url: Url,
    pub jwt: Secret<String>,
    pub payload: Secret<String>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CompleteThreeDsActionLink {
    #[serde(rename = "complete3dsChallenge")]
    pub complete_three_ds_challenge: ActionLink,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum IssuerResponse {
    Challenged,
    Frictionless,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DDCResponse {
    pub device_data_collection: DDCToken,
    #[serde(rename = "_actions")]
    pub actions: DDCActionLink,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DDCToken {
    pub jwt: Secret<String>,
    pub url: Url,
    pub bin: Secret<String>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DDCActionLink {
    #[serde(rename = "supply3dsDeviceData")]
    supply_ddc_data: ActionLink,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum PaymentOutcome {
    #[serde(alias = "authorized", alias = "Authorized")]
    Authorized,
    Refused,
    SentForSettlement,
    SentForRefund,
    FraudHighRisk,
    #[serde(alias = "3dsDeviceDataRequired")]
    ThreeDsDeviceDataRequired,
    SentForCancellation,
    #[serde(alias = "3dsAuthenticationFailed")]
    ThreeDsAuthenticationFailed,
    SentForPartialRefund,
    #[serde(alias = "3dsChallenged")]
    ThreeDsChallenged,
    #[serde(alias = "3dsUnavailable")]
    ThreeDsUnavailable,
}

impl std::fmt::Display for PaymentOutcome {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Authorized => write!(f, "authorized"),
            Self::Refused => write!(f, "refused"),
            Self::SentForSettlement => write!(f, "sentForSettlement"),
            Self::SentForRefund => write!(f, "sentForRefund"),
            Self::FraudHighRisk => write!(f, "fraudHighRisk"),
            Self::ThreeDsDeviceDataRequired => write!(f, "3dsDeviceDataRequired"),
            Self::SentForCancellation => write!(f, "sentForCancellation"),
            Self::ThreeDsAuthenticationFailed => write!(f, "3dsAuthenticationFailed"),
            Self::SentForPartialRefund => write!(f, "sentForPartialRefund"),
            Self::ThreeDsChallenged => write!(f, "3dsChallenged"),
            Self::ThreeDsUnavailable => write!(f, "3dsUnavailable"),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SelfLink {
    #[serde(rename = "self")]
    pub self_link: SelfLinkInner,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SelfLinkInner {
    pub href: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionLinks {
    supply_3ds_device_data: Option<ActionLink>,
    settle_payment: Option<ActionLink>,
    partially_settle_payment: Option<ActionLink>,
    refund_payment: Option<ActionLink>,
    partially_refund_payment: Option<ActionLink>,
    cancel_payment: Option<ActionLink>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ActionLink {
    pub href: String,
    pub method: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Fraud {
    pub outcome: FraudOutcome,
    pub score: f32,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum FraudOutcome {
    LowRisk,
    HighRisk,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WorldpayEventResponse {
    pub last_event: EventType,
    #[serde(rename = "_links", skip_serializing_if = "Option::is_none")]
    pub links: Option<EventLinks>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EventType {
    SentForAuthorization,
    #[serde(alias = "Authorized")]
    Authorized,
    #[serde(alias = "Sent for Settlement")]
    SentForSettlement,
    Settled,
    SettlementFailed,
    Cancelled,
    Error,
    Expired,
    Refused,
    #[serde(alias = "Sent for Refund")]
    SentForRefund,
    Refunded,
    RefundFailed,
    #[serde(other)]
    Unknown,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct EventLinks {
    #[serde(rename = "payments:events", skip_serializing_if = "Option::is_none")]
    pub events: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct PaymentLink {
    pub href: String,
}

pub fn get_resource_id<T, F>(
    response: WorldpayPaymentsResponse,
    connector_transaction_id: Option<String>,
    transform_fn: F,
) -> Result<T, error_stack::Report<errors::ConnectorError>>
where
    F: Fn(String) -> T,
{
    let optional_reference_id = response
        .other_fields
        .as_ref()
        .and_then(|other_fields| match other_fields {
            WorldpayPaymentResponseFields::AuthorizedResponse(res) => res
                .links
                .as_ref()
                .and_then(|link| link.self_link.href.rsplit_once('/').map(|(_, h)| h)),
            WorldpayPaymentResponseFields::DDCResponse(res) => {
                res.actions.supply_ddc_data.href.split('/').nth_back(1)
            }
            WorldpayPaymentResponseFields::ThreeDsChallenged(res) => res
                .actions
                .complete_three_ds_challenge
                .href
                .split('/')
                .nth_back(1),
            WorldpayPaymentResponseFields::FraudHighRisk(_)
            | WorldpayPaymentResponseFields::RefusedResponse(_) => None,
        })
        .map(|href| {
            urlencoding::decode(href)
                .map(|s| transform_fn(s.into_owned()))
                .change_context(errors::ConnectorError::ResponseHandlingFailed)
        })
        .transpose()?;
    optional_reference_id
        .or_else(|| connector_transaction_id.map(transform_fn))
        .ok_or_else(|| {
            errors::ConnectorError::MissingRequiredField {
                field_name: "_links.self.href",
            }
            .into()
        })
}

pub struct ResponseIdStr {
    pub id: String,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Issuer {
    pub authorization_code: Secret<String>,
}

impl Issuer {
    pub fn new(code: String) -> Self {
        Self {
            authorization_code: Secret::new(code),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentsResPaymentInstrument {
    #[serde(rename = "type")]
    pub payment_instrument_type: String,
    pub card_bin: Option<String>,
    pub last_four: Option<String>,
    pub expiry_date: Option<ExpiryDate>,
    pub card_brand: Option<String>,
    pub funding_type: Option<String>,
    pub category: Option<String>,
    pub issuer_name: Option<String>,
    pub payment_account_reference: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RiskFactorsInner {
    #[serde(rename = "type")]
    pub risk_type: RiskType,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub detail: Option<Detail>,
    pub risk: Risk,
}

impl RiskFactorsInner {
    pub fn new(risk_type: RiskType, risk: Risk) -> Self {
        Self {
            risk_type,
            detail: None,
            risk,
        }
    }
}

#[derive(
    Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
#[serde(rename_all = "camelCase")]
pub enum RiskType {
    #[default]
    Avs,
    Cvc,
    RiskProfile,
}

#[derive(
    Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
#[serde(rename_all = "lowercase")]
pub enum Detail {
    #[default]
    Address,
    Postcode,
}

#[derive(
    Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
#[serde(rename_all = "camelCase")]
pub enum Risk {
    #[default]
    NotChecked,
    NotMatched,
    NotSupplied,
    VerificationFailed,
}

#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct PaymentsResponseScheme {
    pub reference: String,
}

impl PaymentsResponseScheme {
    pub fn new(reference: String) -> Self {
        Self { reference }
    }
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorldpayErrorResponse {
    pub error_name: String,
    pub message: String,
    pub validation_errors: Option<serde_json::Value>,
}

impl WorldpayErrorResponse {
    pub fn default(status_code: u16) -> Self {
        match status_code {
            code @ 404 => Self {
                error_name: format!("{code} Not found"),
                message: "Resource not found".to_string(),
                validation_errors: None,
            },
            code => Self {
                error_name: code.to_string(),
                message: "Unknown error".to_string(),
                validation_errors: None,
            },
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WorldpayWebhookTransactionId {
    pub event_details: EventDetails,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EventDetails {
    #[serde(rename = "type")]
    pub event_type: EventType,
    pub transaction_reference: String,
    /// Mandate's token
    pub token: Option<MandateToken>,
    /// Network transaction ID
    pub scheme_reference: Option<Secret<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WorldpayWebhookEventType {
    pub event_id: String,
    pub event_timestamp: String,
    pub event_details: EventDetails,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum WorldpayWebhookStatus {
    SentForSettlement,
    Authorized,
    SentForAuthorization,
    Cancelled,
    Error,
    Expired,
    Refused,
    SentForRefund,
    RefundFailed,
}

/// Worldpay's unique reference ID for a request
pub(super) const WP_CORRELATION_ID: &str = "WP-CorrelationId";


impl Event for AuditEvent {
    type EventType = super::EventType;

    fn timestamp(&self) -> PrimitiveDateTime {
        self.created_at
    }

    fn identifier(&self) -> String {
        let event_type = match &self.event_type {
            AuditEventType::Error { .. } => "error",
            AuditEventType::PaymentCreated => "payment_created",
            AuditEventType::PaymentConfirm { .. } => "payment_confirm",
            AuditEventType::ConnectorDecided => "connector_decided",
            AuditEventType::ConnectorCalled => "connector_called",
            AuditEventType::PaymentCapture { .. } => "payment_capture",
            AuditEventType::RefundCreated => "refund_created",
            AuditEventType::RefundSuccess => "refund_success",
            AuditEventType::RefundFail => "refund_fail",
            AuditEventType::PaymentCancelled { .. } => "payment_cancelled",
            AuditEventType::PaymentUpdate { .. } => "payment_update",
            AuditEventType::PaymentApprove => "payment_approve",
            AuditEventType::PaymentCreate => "payment_create",
            AuditEventType::PaymentStatus => "payment_status",
            AuditEventType::PaymentCompleteAuthorize => "payment_complete_authorize",
            AuditEventType::PaymentReject { .. } => "payment_rejected",
        };
        format!(
            "{event_type}-{}",
            self.timestamp().assume_utc().unix_timestamp_nanos()
        )
    }

    fn class(&self) -> Self::EventType {
        super::EventType::AuditEvent
    }
}


pub struct AmountBreakdown {
    item_total: OrderAmount,
    tax_total: Option<OrderAmount>,
    shipping: Option<OrderAmount>,
}


pub struct RevenueRecoveryMetadata {
    /// The maximum number of retries allowed for an invoice. This limit is set by the merchant for each `billing connector`.Once this limit is reached, no further retries will be attempted.
    pub max_retry_count: u16,
    /// Maximum number of `billing connector` retries before revenue recovery can start executing retries.
    pub billing_connector_retry_threshold: u16,
    /// Billing account reference id is payment gateway id at billing connector end.
    /// Merchants need to provide a mapping between these merchant connector account and the corresponding
    /// account reference IDs for each `billing connector`.
    pub billing_account_reference: BillingAccountReference,
}


OpenAPI Block Path: components.schemas.GenericLinkUiConfig
{
  "type": "object",
  "description": "Object for GenericLinkUiConfig",
  "properties": {
    "logo": {
      "type": "string",
      "description": "Merchant's display logo",
      "example": "https://hyperswitch.io/favicon.ico",
      "nullable": true,
      "maxLength": 255
    },
    "merchant_name": {
      "type": "string",
      "description": "Custom merchant name for the link",
      "example": "Hyperswitch",
      "nullable": true,
      "maxLength": 255
    },
    "theme": {
      "type": "string",
      "description": "Primary color to be used in the form represented in hex format",
      "example": "#4285F4",
      "nullable": true,
      "maxLength": 255
    }
  }
}


pub struct AttachPayoutAccountWorkflow;


File: crates/hyperswitch_connectors/src/connectors/wellsfargopayout/transformers.rs
 Public structs: 8

use common_enums::{AttemptStatus, RefundStatus};
use common_utils::types::StringMinorUnit;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors::ConnectorError;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::PaymentsAuthorizeRequestData as _,
};

//TODO: Fill the struct with respective fields
pub struct WellsfargopayoutRouterData<T> {
    pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

impl<T> From<(StringMinorUnit, T)> for WellsfargopayoutRouterData<T> {
    fn from((amount, item): (StringMinorUnit, T)) -> Self {
        //Todo :  use utils to convert the amount to the type of amount that a connector accepts
        Self {
            amount,
            router_data: item,
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, PartialEq)]
pub struct WellsfargopayoutPaymentsRequest {
    amount: StringMinorUnit,
    card: WellsfargopayoutCard,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct WellsfargopayoutCard {
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
    complete: bool,
}

impl TryFrom<&WellsfargopayoutRouterData<&PaymentsAuthorizeRouterData>>
    for WellsfargopayoutPaymentsRequest
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: &WellsfargopayoutRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = WellsfargopayoutCard {
                    number: req_card.card_number,
                    expiry_month: req_card.card_exp_month,
                    expiry_year: req_card.card_exp_year,
                    cvc: req_card.card_cvc,
                    complete: item.router_data.request.is_auto_capture()?,
                };
                Ok(Self {
                    amount: item.amount.clone(),
                    card,
                })
            }
            _ => Err(ConnectorError::NotImplemented("Payment methods".to_string()).into()),
        }
    }
}

//TODO: Fill the struct with respective fields
// Auth Struct
pub struct WellsfargopayoutAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for WellsfargopayoutAuthType {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                api_key: api_key.to_owned(),
            }),
            _ => Err(ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}
// PaymentsResponse
//TODO: Append the remaining status flags
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum WellsfargopayoutPaymentStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<WellsfargopayoutPaymentStatus> for AttemptStatus {
    fn from(item: WellsfargopayoutPaymentStatus) -> Self {
        match item {
            WellsfargopayoutPaymentStatus::Succeeded => Self::Charged,
            WellsfargopayoutPaymentStatus::Failed => Self::Failure,
            WellsfargopayoutPaymentStatus::Processing => Self::Authorizing,
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct WellsfargopayoutPaymentsResponse {
    status: WellsfargopayoutPaymentStatus,
    id: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, WellsfargopayoutPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, WellsfargopayoutPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
// REFUND :
// Type definition for RefundRequest
#[derive(Default, Debug, Serialize)]
pub struct WellsfargopayoutRefundRequest {
    pub amount: StringMinorUnit,
}

impl<F> TryFrom<&WellsfargopayoutRouterData<&RefundsRouterData<F>>>
    for WellsfargopayoutRefundRequest
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: &WellsfargopayoutRouterData<&RefundsRouterData<F>>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: item.amount.to_owned(),
        })
    }
}

// Type definition for Refund Response

#[allow(dead_code)]
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
pub enum WellsfargopayoutRefundStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<WellsfargopayoutRefundStatus> for RefundStatus {
    fn from(item: WellsfargopayoutRefundStatus) -> Self {
        match item {
            WellsfargopayoutRefundStatus::Succeeded => Self::Success,
            WellsfargopayoutRefundStatus::Failed => Self::Failure,
            WellsfargopayoutRefundStatus::Processing => Self::Pending,
            //TODO: Review mapping
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse {
    id: String,
    status: WellsfargopayoutRefundStatus,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct WellsfargopayoutErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
}


pub async fn card_iin_info(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<String>,
    payload: web::Query<api_models::cards_info::CardsInfoRequestParams>,
) -> impl Responder {
    let card_iin = path.into_inner();
    let request_params = payload.into_inner();

    let payload = api_models::cards_info::CardsInfoRequest {
        client_secret: request_params.client_secret,
        card_iin,
    };

    let api_auth = auth::ApiKeyAuth::default();

    let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth)
    {
        Ok((auth, _auth_flow)) => (auth, _auth_flow),
        Err(e) => return api::log_and_return_error_response(e),
    };

    Box::pin(api::server_wrap(
        Flow::CardsInfo,
        state,
        &req,
        payload,
        |state, auth, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            cards_info::retrieve_card_info(state, merchant_context, req)
        },
        &*auth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


File: crates/euclid/src/types.rs
 Public functions: 4
 Public structs: 4

pub mod transformers;

use common_utils::types::MinorUnit;
use euclid_macros::EnumNums;
use serde::{Deserialize, Serialize};
use strum::VariantNames;

use crate::{
    dssa::types::EuclidAnalysable,
    enums,
    frontend::{
        ast,
        dir::{
            enums::{CustomerDeviceDisplaySize, CustomerDevicePlatform, CustomerDeviceType},
            DirKeyKind, DirValue, EuclidDirFilter,
        },
    },
};

pub type Metadata = std::collections::HashMap<String, serde_json::Value>;

#[derive(
    Debug,
    Clone,
    EnumNums,
    Hash,
    PartialEq,
    Eq,
    strum::Display,
    strum::VariantNames,
    strum::EnumString,
)]
pub enum EuclidKey {
    #[strum(serialize = "payment_method")]
    PaymentMethod,
    #[strum(serialize = "card_bin")]
    CardBin,
    #[strum(serialize = "metadata")]
    Metadata,
    #[strum(serialize = "mandate_type")]
    MandateType,
    #[strum(serialize = "mandate_acceptance_type")]
    MandateAcceptanceType,
    #[strum(serialize = "payment_type")]
    PaymentType,
    #[strum(serialize = "payment_method_type")]
    PaymentMethodType,
    #[strum(serialize = "card_network")]
    CardNetwork,
    #[strum(serialize = "authentication_type")]
    AuthenticationType,
    #[strum(serialize = "capture_method")]
    CaptureMethod,
    #[strum(serialize = "amount")]
    PaymentAmount,
    #[strum(serialize = "currency")]
    PaymentCurrency,
    #[strum(serialize = "country", to_string = "business_country")]
    BusinessCountry,
    #[strum(serialize = "billing_country")]
    BillingCountry,
    #[strum(serialize = "business_label")]
    BusinessLabel,
    #[strum(serialize = "setup_future_usage")]
    SetupFutureUsage,
    #[strum(serialize = "issuer_name")]
    IssuerName,
    #[strum(serialize = "issuer_country")]
    IssuerCountry,
    #[strum(serialize = "acquirer_country")]
    AcquirerCountry,
    #[strum(serialize = "acquirer_fraud_rate")]
    AcquirerFraudRate,
    #[strum(serialize = "customer_device_type")]
    CustomerDeviceType,
    #[strum(serialize = "customer_device_display_size")]
    CustomerDeviceDisplaySize,
    #[strum(serialize = "customer_device_platform")]
    CustomerDevicePlatform,
}

impl EuclidDirFilter for DummyOutput {
    const ALLOWED: &'static [DirKeyKind] = &[
        DirKeyKind::AuthenticationType,
        DirKeyKind::PaymentMethod,
        DirKeyKind::CardType,
        DirKeyKind::PaymentCurrency,
        DirKeyKind::CaptureMethod,
        DirKeyKind::AuthenticationType,
        DirKeyKind::CardBin,
        DirKeyKind::PayLaterType,
        DirKeyKind::PaymentAmount,
        DirKeyKind::MetaData,
        DirKeyKind::MandateAcceptanceType,
        DirKeyKind::MandateType,
        DirKeyKind::PaymentType,
        DirKeyKind::SetupFutureUsage,
    ];
}
impl EuclidAnalysable for DummyOutput {
    fn get_dir_value_for_analysis(&self, rule_name: String) -> Vec<(DirValue, Metadata)> {
        self.outputs
            .iter()
            .map(|dummyc| {
                let metadata_key = "MetadataKey".to_string();
                let metadata_value = dummyc;
                (
                    DirValue::MetaData(MetadataValue {
                        key: metadata_key.clone(),
                        value: metadata_value.clone(),
                    }),
                    std::collections::HashMap::from_iter([(
                        "DUMMY_OUTPUT".to_string(),
                        serde_json::json!({
                            "rule_name":rule_name,
                             "Metadata_Key" :metadata_key,
                             "Metadata_Value" : metadata_value,
                        }),
                    )]),
                )
            })
            .collect()
    }
}
#[derive(Debug, Clone, Serialize)]
pub struct DummyOutput {
    pub outputs: Vec<String>,
}

#[derive(Debug, Clone, serde::Serialize, strum::Display)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum DataType {
    Number,
    EnumVariant,
    MetadataValue,
    StrValue,
}

impl EuclidKey {
    pub fn key_type(&self) -> DataType {
        match self {
            Self::PaymentMethod => DataType::EnumVariant,
            Self::CardBin => DataType::StrValue,
            Self::Metadata => DataType::MetadataValue,
            Self::PaymentMethodType => DataType::EnumVariant,
            Self::CardNetwork => DataType::EnumVariant,
            Self::AuthenticationType => DataType::EnumVariant,
            Self::CaptureMethod => DataType::EnumVariant,
            Self::PaymentAmount => DataType::Number,
            Self::PaymentCurrency => DataType::EnumVariant,
            Self::BusinessCountry => DataType::EnumVariant,
            Self::BillingCountry => DataType::EnumVariant,
            Self::MandateType => DataType::EnumVariant,
            Self::MandateAcceptanceType => DataType::EnumVariant,
            Self::PaymentType => DataType::EnumVariant,
            Self::BusinessLabel => DataType::StrValue,
            Self::SetupFutureUsage => DataType::EnumVariant,
            Self::IssuerName => DataType::StrValue,
            Self::IssuerCountry => DataType::EnumVariant,
            Self::AcquirerCountry => DataType::EnumVariant,
            Self::AcquirerFraudRate => DataType::Number,
            Self::CustomerDeviceType => DataType::EnumVariant,
            Self::CustomerDeviceDisplaySize => DataType::EnumVariant,
            Self::CustomerDevicePlatform => DataType::EnumVariant,
        }
    }
}

enums::collect_variants!(EuclidKey);

#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NumValueRefinement {
    NotEqual,
    GreaterThan,
    LessThan,
    GreaterThanEqual,
    LessThanEqual,
}

impl From<ast::ComparisonType> for Option<NumValueRefinement> {
    fn from(comp_type: ast::ComparisonType) -> Self {
        match comp_type {
            ast::ComparisonType::Equal => None,
            ast::ComparisonType::NotEqual => Some(NumValueRefinement::NotEqual),
            ast::ComparisonType::GreaterThan => Some(NumValueRefinement::GreaterThan),
            ast::ComparisonType::LessThan => Some(NumValueRefinement::LessThan),
            ast::ComparisonType::LessThanEqual => Some(NumValueRefinement::LessThanEqual),
            ast::ComparisonType::GreaterThanEqual => Some(NumValueRefinement::GreaterThanEqual),
        }
    }
}

impl From<NumValueRefinement> for ast::ComparisonType {
    fn from(value: NumValueRefinement) -> Self {
        match value {
            NumValueRefinement::NotEqual => Self::NotEqual,
            NumValueRefinement::LessThan => Self::LessThan,
            NumValueRefinement::GreaterThan => Self::GreaterThan,
            NumValueRefinement::GreaterThanEqual => Self::GreaterThanEqual,
            NumValueRefinement::LessThanEqual => Self::LessThanEqual,
        }
    }
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct StrValue {
    pub value: String,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct MetadataValue {
    pub key: String,
    pub value: String,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct NumValue {
    pub number: MinorUnit,
    pub refinement: Option<NumValueRefinement>,
}

impl NumValue {
    pub fn fits(&self, other: &Self) -> bool {
        let this_num = self.number;
        let other_num = other.number;

        match (&self.refinement, &other.refinement) {
            (None, None) => this_num == other_num,

            (Some(NumValueRefinement::GreaterThan), None) => other_num > this_num,

            (Some(NumValueRefinement::LessThan), None) => other_num < this_num,

            (Some(NumValueRefinement::NotEqual), Some(NumValueRefinement::NotEqual)) => {
                other_num == this_num
            }

            (Some(NumValueRefinement::GreaterThan), Some(NumValueRefinement::GreaterThan)) => {
                other_num > this_num
            }
            (Some(NumValueRefinement::LessThan), Some(NumValueRefinement::LessThan)) => {
                other_num < this_num
            }

            (Some(NumValueRefinement::GreaterThanEqual), None) => other_num >= this_num,
            (Some(NumValueRefinement::LessThanEqual), None) => other_num <= this_num,
            (
                Some(NumValueRefinement::GreaterThanEqual),
                Some(NumValueRefinement::GreaterThanEqual),
            ) => other_num >= this_num,

            (Some(NumValueRefinement::LessThanEqual), Some(NumValueRefinement::LessThanEqual)) => {
                other_num <= this_num
            }

            _ => false,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EuclidValue {
    PaymentMethod(enums::PaymentMethod),
    CardBin(StrValue),
    Metadata(MetadataValue),
    PaymentMethodType(enums::PaymentMethodType),
    CardNetwork(enums::CardNetwork),
    AuthenticationType(enums::AuthenticationType),
    CaptureMethod(enums::CaptureMethod),
    PaymentType(enums::PaymentType),
    MandateAcceptanceType(enums::MandateAcceptanceType),
    MandateType(enums::MandateType),
    PaymentAmount(NumValue),
    PaymentCurrency(enums::Currency),
    BusinessCountry(enums::Country),
    BillingCountry(enums::Country),
    BusinessLabel(StrValue),
    SetupFutureUsage(enums::SetupFutureUsage),
    IssuerName(StrValue),
    IssuerCountry(enums::Country),
    AcquirerCountry(enums::Country),
    AcquirerFraudRate(NumValue),
    CustomerDeviceType(CustomerDeviceType),
    CustomerDeviceDisplaySize(CustomerDeviceDisplaySize),
    CustomerDevicePlatform(CustomerDevicePlatform),
}

impl EuclidValue {
    pub fn get_num_value(&self) -> Option<NumValue> {
        match self {
            Self::PaymentAmount(val) => Some(val.clone()),
            _ => None,
        }
    }

    pub fn get_key(&self) -> EuclidKey {
        match self {
            Self::PaymentMethod(_) => EuclidKey::PaymentMethod,
            Self::CardBin(_) => EuclidKey::CardBin,
            Self::Metadata(_) => EuclidKey::Metadata,
            Self::PaymentMethodType(_) => EuclidKey::PaymentMethodType,
            Self::MandateType(_) => EuclidKey::MandateType,
            Self::PaymentType(_) => EuclidKey::PaymentType,
            Self::MandateAcceptanceType(_) => EuclidKey::MandateAcceptanceType,
            Self::CardNetwork(_) => EuclidKey::CardNetwork,
            Self::AuthenticationType(_) => EuclidKey::AuthenticationType,
            Self::CaptureMethod(_) => EuclidKey::CaptureMethod,
            Self::PaymentAmount(_) => EuclidKey::PaymentAmount,
            Self::PaymentCurrency(_) => EuclidKey::PaymentCurrency,
            Self::BusinessCountry(_) => EuclidKey::BusinessCountry,
            Self::BillingCountry(_) => EuclidKey::BillingCountry,
            Self::BusinessLabel(_) => EuclidKey::BusinessLabel,
            Self::SetupFutureUsage(_) => EuclidKey::SetupFutureUsage,
            Self::IssuerName(_) => EuclidKey::IssuerName,
            Self::IssuerCountry(_) => EuclidKey::IssuerCountry,
            Self::AcquirerCountry(_) => EuclidKey::AcquirerCountry,
            Self::AcquirerFraudRate(_) => EuclidKey::AcquirerFraudRate,
            Self::CustomerDeviceType(_) => EuclidKey::CustomerDeviceType,
            Self::CustomerDeviceDisplaySize(_) => EuclidKey::CustomerDeviceDisplaySize,
            Self::CustomerDevicePlatform(_) => EuclidKey::CustomerDevicePlatform,
        }
    }
}

#[cfg(test)]
mod global_type_tests {
    use super::*;

    #[test]
    fn test_num_value_fits_greater_than() {
        let val1 = NumValue {
            number: MinorUnit::new(10),
            refinement: Some(NumValueRefinement::GreaterThan),
        };
        let val2 = NumValue {
            number: MinorUnit::new(30),
            refinement: Some(NumValueRefinement::GreaterThan),
        };

        assert!(val1.fits(&val2))
    }

    #[test]
    fn test_num_value_fits_less_than() {
        let val1 = NumValue {
            number: MinorUnit::new(30),
            refinement: Some(NumValueRefinement::LessThan),
        };
        let val2 = NumValue {
            number: MinorUnit::new(10),
            refinement: Some(NumValueRefinement::LessThan),
        };

        assert!(val1.fits(&val2));
    }
}


pub struct WorldpayErrorResponse {
    pub error_name: String,
    pub message: String,
    pub validation_errors: Option<serde_json::Value>,
}


pub struct MollieMetadata {
    pub order_id: String,
}


impl api::RefundSync for UnifiedAuthenticationService {}

impl api::RefundSync for for UnifiedAuthenticationService


pub struct AirwallexCardPaymentOptions {
    auto_capture: bool,
}


pub struct WellsfargoPaymentInstrument {
    id: Secret<String>,
}


pub fn is_update_required(
        &self,
        data_stored_in_vault: api::payment_methods::CardDetailFromLocker,
    ) -> bool {
        //if the expiry year and month in the vault are not the same as the ones in the requestor data,
        //then we need to update the vault data with the updated expiry year and month.
        !((data_stored_in_vault.expiry_year.unwrap_or_default() == self.expiry_year)
            && (data_stored_in_vault.expiry_month.unwrap_or_default() == self.expiry_month))
    }


pub fn calculate_signature(
    enctype: EncodingType,
    signature_data: ZslSignatureType,
) -> Result<Secret<String>, error_stack::Report<errors::ConnectorError>> {
    let signature_data = match signature_data {
        ZslSignatureType::RequestSignature {
            txn_amt,
            ccy,
            mer_ref,
            mer_id,
            mer_txn_date,
            key,
        } => format!("{txn_amt}{ccy}{mer_ref}{mer_id}{mer_txn_date}{key}"),
        ZslSignatureType::ResponseSignature {
            status,
            txn_url,
            mer_ref,
            mer_id,
            key,
        } => {
            format!("{status}{txn_url}{mer_ref}{mer_id}{key}")
        }
        ZslSignatureType::WebhookSignature {
            status,
            txn_id,
            txn_date,
            paid_ccy,
            paid_amt,
            mer_ref,
            mer_id,
            key,
        } => format!("{status}{txn_id}{txn_date}{paid_ccy}{paid_amt}{mer_ref}{mer_id}{key}"),
    };
    let message = signature_data.as_bytes();

    let encoded_data = match enctype {
        EncodingType::MD5 => hex::encode(
            common_utils::crypto::Md5
                .generate_digest(message)
                .change_context(errors::ConnectorError::RequestEncodingFailed)?,
        ),
        EncodingType::Sha1 => {
            hex::encode(digest::digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, message))
        }
    };
    Ok(Secret::new(encoded_data))
}


Web Documentation: Data Security | Hyperswitch
# Type: Web Doc

At Hyperswitch, we prioritize data security and adhere to PCI DSS standards to protect sensitive information. Our platform is engineered with a robust multi-layered encryption framework to secure sensitive data, including API credentials, RSA certificates, database passwords, and Personally Identifiable Information (PII), throughout its lifecycle.
Benefits of Our Security Framework
Compliance and Trust
: We comply with PCI DSS 4.0 and ISO 27001:2022 standards, ensuring international best practices for data protection.
Enhanced Data Security
: Hyperswitch employs advanced encryption layers to secure data during transmission and storage.
Fraud Prevention
: Through secure card vaulting and tokenization, we minimize fraud risks.
Operational Transparency
: With merchant-specific encryption, we ensure your data remains confidential and protected.
Handling Sensitive Data
The Hyperswitch application employs multiple layers of encryption to safeguard sensitive card information during transmission between components as explained below. Our security framework is designed to meet PCI standards, ensuring maximum protection and confidentiality for all card-related data.
1. Accepting Card Data
Card information is initially received from the Hyperswitch SDK. This data is encrypted using SSL/TLS protocols, ensuring end-to-end encryption during transmission between the SDK and the Hyperswitch backend. This guarantees that sensitive card data is secure from the point of collection.
2. Storing Card Data (Vaulting)
When a payment is made using a saved card, the card details are securely stored in the Hyperswitch Card Vault. This process involves multiple steps to ensure the confidentiality and integrity of the data:
Card Data Preparation:
Signing for Integrity
: The Hyperswitch App Server signs the card details using its private key, ensuring the data has not been altered.
Encryption for Confidentiality
: The signed details are encrypted using the public key of the Card Vault (locker), securing the data during transmission.
Data Storage Process:
Validation
: The Hyperswitch Card Vault validates the signed data using the public key of the Hyperswitch App Server. This step ensures the integrity of the transmitted data.
Decryption
: The Card Vault decrypts the received data to make it usable for storage.
Secure Storage
:
The decrypted data is re-encrypted internally using AES encryption.
This securely encrypted data is then stored in the database.
The database itself is encrypted at rest, providing an additional layer of security for the stored information.
For more technical details about how the Hyperswitch Card Vault manages encryption and decryption, you can visit the
Hyperswitch Card Vault GitHub repository
.
3. Using Card Data for Analytics and Payment Operations
To provide insights and analytics without compromising sensitive information, only partially masked card details (e.g., the first 4 and last 4 digits) are sent to the Hyperswitch Control Centre. This approach offers merchants a high-level view of transactions while preserving customer confidentiality.
Data Encryption Overview
At Hyperswitch, we place the utmost importance on safeguarding sensitive data, including
external API credentials, customer information, and card details
. Our application employs a multi-layered encryption approach to ensure security during data transmission and storage.
Masked Transmission
: All sensitive data remains masked during transmission and is never permanently stored on local systems.
Multi-Layered Encryption
: Data is encrypted at every critical juncture, ensuring robust protection against breaches.
Key Management System (KMS) Encryption
Hyperswitch employs AWS Key Management System (KMS) to securely manage sensitive keys required for the application’s operation.
Startup Encryption
: Sensitive keys, such as database passwords and RSA certificates, are encrypted at the startup.
Secure Storage
: These encrypted keys are securely stored in environment variables or configuration files, ensuring their confidentiality and protection against unauthorized access.
Key Manager Service Encryption
Merchant-Specific Encryption
Hyperswitch utilizes a
Key Manager Service
to ensure the secure generation and storage of a
unique Data Encryption Key (DEK)
for each merchant.
These merchant-specific DEKs undergo further encryption using a secrets manager, such as
AWS KMS
, to provide an additional layer of security.
Data Encryption for Each Merchant Account
Data associated with individual merchant accounts is encrypted using the unique DEK for that merchant. This approach ensures robust protection of:
Connector API Keys
: Critical credentials for third-party integrations.
Confidential Merchant Information
: Business-sensitive data that needs stringent security.
Customer Personally Identifiable Information (PII)
: Data such as customer names, email addresses, and other sensitive details.
The encryption process ensures:
Data Segmentation and Authorized Access Only
: Each merchant's data is encrypted with their specific DEK, ensuring that each set of data remains protected and is accessible only to authorized parties associated with that merchant. Data can only be decrypted by entities possessing the appropriate credentials tied to that merchant's DEK.
Concealing Sensitive Data in Logs
At Hyperswitch, we take extra care to protect sensitive information, even in system logs. Our application framework uses a
wrapper type
to classify all sensitive data as
Secret
.
Leveraging Rust's Advantages
: Hyperswitch uses Rust, a strongly typed programming language, to create a robust mechanism (
Secret<T>
) for handling sensitive data.
Masked Logging
: Instead of exposing sensitive details such as passwords or Personally Identifiable Information (PII), we log only the data type (e.g.,
*** alloc::string::String ***
).
Source-Level Protection
: By masking sensitive data at its source, we ensure that sensitive information is never inadvertently exposed, even in debug outputs or logs.
Database at Rest Encryption
At Hyperswitch, we encrypt database instances in our cloud-hosted environments to protect sensitive information, including card details and merchant data. For self-hosted setups, we recommend adopting similar encryption practices to ensure robust security.
10 months ago
Was this helpful?

Data Security | Hyperswitch


pub trait OperationSessionSetters<F> {
    // Setter functions for PaymentData
    fn set_payment_intent(&mut self, payment_intent: storage::PaymentIntent);
    #[cfg(feature = "v2")]
    fn set_client_secret(&mut self, client_secret: Option<Secret<String>>);
    fn set_payment_attempt(&mut self, payment_attempt: storage::PaymentAttempt);
    fn set_payment_method_data(&mut self, payment_method_data: Option<domain::PaymentMethodData>);
    fn set_payment_method_token(&mut self, payment_method_token: Option<PaymentMethodToken>);
    fn set_email_if_not_present(&mut self, email: pii::Email);
    fn set_payment_method_id_in_attempt(&mut self, payment_method_id: Option<String>);
    fn set_pm_token(&mut self, token: String);
    fn set_connector_customer_id(&mut self, customer_id: Option<String>);
    fn push_sessions_token(&mut self, token: api::SessionToken);
    fn set_surcharge_details(&mut self, surcharge_details: Option<types::SurchargeDetails>);
    fn set_merchant_connector_id_in_attempt(
        &mut self,
        merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    );
    fn set_card_network(&mut self, card_network: enums::CardNetwork);
    fn set_co_badged_card_data(
        &mut self,
        debit_routing_output: &api_models::open_router::DebitRoutingOutput,
    );
    #[cfg(feature = "v1")]
    fn set_capture_method_in_attempt(&mut self, capture_method: enums::CaptureMethod);
    fn set_frm_message(&mut self, frm_message: FraudCheck);
    fn set_payment_intent_status(&mut self, status: storage_enums::IntentStatus);
    fn set_authentication_type_in_attempt(
        &mut self,
        authentication_type: Option<enums::AuthenticationType>,
    );
    fn set_recurring_mandate_payment_data(
        &mut self,
        recurring_mandate_payment_data:
            hyperswitch_domain_models::router_data::RecurringMandatePaymentData,
    );
    fn set_mandate_id(&mut self, mandate_id: api_models::payments::MandateIds);
    fn set_setup_future_usage_in_payment_intent(
        &mut self,
        setup_future_usage: storage_enums::FutureUsage,
    );

    #[cfg(feature = "v1")]
    fn set_straight_through_algorithm_in_payment_attempt(
        &mut self,
        straight_through_algorithm: serde_json::Value,
    );

    #[cfg(feature = "v2")]
    fn set_prerouting_algorithm_in_payment_intent(
        &mut self,
        straight_through_algorithm: storage::PaymentRoutingInfo,
    );

    fn set_connector_in_payment_attempt(&mut self, connector: Option<String>);

    #[cfg(feature = "v1")]
    fn set_vault_operation(&mut self, vault_operation: domain_payments::VaultOperation);

    #[cfg(feature = "v2")]
    fn set_connector_request_reference_id(&mut self, reference_id: Option<String>);

    fn set_connector_response_reference_id(&mut self, reference_id: Option<String>);

    #[cfg(feature = "v2")]
    fn set_vault_session_details(
        &mut self,
        external_vault_session_details: Option<api::VaultSessionDetails>,
    );
    fn set_routing_approach_in_attempt(&mut self, routing_approach: Option<enums::RoutingApproach>);

    fn set_connector_request_reference_id_in_payment_attempt(
        &mut self,
        connector_request_reference_id: String,
    );
}


impl Responder {
        // safety: This shouldn't panic owing to the data type
        #[allow(clippy::expect_used)]
        let payload = json_payload
            .into_inner()
            .to_vec()
            .pop()
            .expect("Couldn't get GetPaymentIntentMetricRequest");
        let flow = AnalyticsFlow::GetPaymentIntentMetrics;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            payload,
            |state, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let profile_id = auth
                    .profile_id
                    .ok_or(report!(UserErrors::JwtProfileIdMissing))
                    .change_context(AnalyticsError::AccessForbiddenError)?;
                let auth: AuthInfo = AuthInfo::ProfileLevel {
                    org_id: org_id.clone(),
                    merchant_id: merchant_id.clone(),
                    profile_ids: vec![profile_id.clone()],
                };

                let validator_response = request_validator(
                    AnalyticsRequest {
                        payment_intent: Some(req.clone()),
                        ..Default::default()
                    },
                    &state,
                )
                .await?;
                let ex_rates = validator_response;
                analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub async fn get_decrypted_wallet_payment_method_token<F, Req, D>(
    operation: &BoxedOperation<'_, F, Req, D>,
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_data: &mut D,
    connector_call_type_optional: Option<&ConnectorCallType>,
) -> CustomResult<Option<PaymentMethodToken>, errors::ApiErrorResponse>
where
    F: Send + Clone + Sync,
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    if is_operation_confirm(operation)
        && payment_data.get_payment_attempt().payment_method
            == Some(storage_enums::PaymentMethod::Wallet)
    {
        let wallet_type = payment_data
            .get_payment_attempt()
            .payment_method_type
            .get_required_value("payment_method_type")?;

        let wallet: Box<dyn WalletFlow<F, D>> = match wallet_type {
            storage_enums::PaymentMethodType::ApplePay => Box::new(ApplePayWallet),
            storage_enums::PaymentMethodType::Paze => Box::new(PazeWallet),
            storage_enums::PaymentMethodType::GooglePay => Box::new(GooglePayWallet),
            _ => return Ok(None),
        };

        // Check if the wallet has already decrypted the token from the payment data.
        // If a pre-decrypted token is available, use it directly to avoid redundant decryption.
        if let Some(predecrypted_token) = wallet.check_predecrypted_token(payment_data)? {
            logger::debug!("Using predecrypted token for wallet");
            return Ok(Some(predecrypted_token));
        }

        let merchant_connector_account =
            get_merchant_connector_account_for_wallet_decryption_flow::<F, D>(
                state,
                merchant_context,
                payment_data,
                connector_call_type_optional,
            )
            .await?;

        let decide_wallet_flow = &wallet
            .decide_wallet_flow(state, payment_data, &merchant_connector_account)
            .attach_printable("Failed to decide wallet flow")?
            .async_map(|payment_price_data| async move {
                wallet
                    .decrypt_wallet_token(&payment_price_data, payment_data)
                    .await
            })
            .await
            .transpose()
            .attach_printable("Failed to decrypt Wallet token")?;
        Ok(decide_wallet_flow.clone())
    } else {
        Ok(None)
    }
}


pub struct ApplePayRedirectData {}


File: crates/router/src/workflows/payment_sync.rs
 Public functions: 2
 Public structs: 1

use common_utils::ext_traits::{OptionExt, StringExt, ValueExt};
use diesel_models::process_tracker::business_status;
use error_stack::ResultExt;
use router_env::logger;
use scheduler::{
    consumer::{self, types::process_data, workflows::ProcessTrackerWorkflow},
    errors as sch_errors, utils as scheduler_utils,
};

use crate::{
    consts,
    core::{
        errors::StorageErrorExt,
        payments::{self as payment_flows, operations},
    },
    db::StorageInterface,
    errors,
    routes::SessionState,
    services,
    types::{
        api, domain,
        storage::{self, enums},
    },
    utils,
};

pub struct PaymentsSyncWorkflow;

#[async_trait::async_trait]
impl ProcessTrackerWorkflow<SessionState> for PaymentsSyncWorkflow {
    #[cfg(feature = "v2")]
    async fn execute_workflow<'a>(
        &'a self,
        _state: &'a SessionState,
        _process: storage::ProcessTracker,
    ) -> Result<(), sch_errors::ProcessTrackerError> {
        todo!()
    }

    #[cfg(feature = "v1")]
    async fn execute_workflow<'a>(
        &'a self,
        state: &'a SessionState,
        process: storage::ProcessTracker,
    ) -> Result<(), sch_errors::ProcessTrackerError> {
        let db: &dyn StorageInterface = &*state.store;
        let tracking_data: api::PaymentsRetrieveRequest = process
            .tracking_data
            .clone()
            .parse_value("PaymentsRetrieveRequest")?;
        let key_manager_state = &state.into();
        let key_store = db
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                tracking_data
                    .merchant_id
                    .as_ref()
                    .get_required_value("merchant_id")?,
                &db.get_master_key().to_vec().into(),
            )
            .await?;

        let merchant_account = db
            .find_merchant_account_by_merchant_id(
                key_manager_state,
                tracking_data
                    .merchant_id
                    .as_ref()
                    .get_required_value("merchant_id")?,
                &key_store,
            )
            .await?;

        let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
            merchant_account.clone(),
            key_store.clone(),
        )));
        // TODO: Add support for ReqState in PT flows
        let (mut payment_data, _, customer, _, _) =
            Box::pin(payment_flows::payments_operation_core::<
                api::PSync,
                _,
                _,
                _,
                payment_flows::PaymentData<api::PSync>,
            >(
                state,
                state.get_req_state(),
                &merchant_context,
                None,
                operations::PaymentStatus,
                tracking_data.clone(),
                payment_flows::CallConnectorAction::Trigger,
                services::AuthFlow::Client,
                None,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            ))
            .await?;

        let terminal_status = [
            enums::AttemptStatus::RouterDeclined,
            enums::AttemptStatus::Charged,
            enums::AttemptStatus::AutoRefunded,
            enums::AttemptStatus::Voided,
            enums::AttemptStatus::VoidFailed,
            enums::AttemptStatus::CaptureFailed,
            enums::AttemptStatus::Failure,
        ];
        match &payment_data.payment_attempt.status {
            status if terminal_status.contains(status) => {
                state
                    .store
                    .as_scheduler()
                    .finish_process_with_business_status(process, business_status::COMPLETED_BY_PT)
                    .await?
            }
            _ => {
                let connector = payment_data
                    .payment_attempt
                    .connector
                    .clone()
                    .ok_or(sch_errors::ProcessTrackerError::MissingRequiredField)?;

                let is_last_retry = retry_sync_task(
                    db,
                    connector,
                    payment_data.payment_attempt.merchant_id.clone(),
                    process,
                )
                .await?;

                // If the payment status is still processing and there is no connector transaction_id
                // then change the payment status to failed if all retries exceeded
                if is_last_retry
                    && payment_data.payment_attempt.status == enums::AttemptStatus::Pending
                    && payment_data
                        .payment_attempt
                        .connector_transaction_id
                        .as_ref()
                        .is_none()
                {
                    let payment_intent_update = hyperswitch_domain_models::payments::payment_intent::PaymentIntentUpdate::PGStatusUpdate { status: api_models::enums::IntentStatus::Failed,updated_by: merchant_account.storage_scheme.to_string(), incremental_authorization_allowed: Some(false), feature_metadata: payment_data.payment_intent.feature_metadata.clone().map(masking::Secret::new), };
                    let payment_attempt_update =
                        hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptUpdate::ErrorUpdate {
                            connector: None,
                            status: api_models::enums::AttemptStatus::Failure,
                            error_code: None,
                            error_message: None,
                            error_reason: Some(Some(
                                consts::REQUEST_TIMEOUT_ERROR_MESSAGE_FROM_PSYNC.to_string(),
                            )),
                            amount_capturable: Some(common_utils::types::MinorUnit::new(0)),
                            updated_by: merchant_account.storage_scheme.to_string(),
                            unified_code: None,
                            unified_message: None,
                            connector_transaction_id: None,
                            payment_method_data: None,
                            authentication_type: None,
                            issuer_error_code: None,
                            issuer_error_message: None,
                        };

                    payment_data.payment_attempt = db
                        .update_payment_attempt_with_attempt_id(
                            payment_data.payment_attempt,
                            payment_attempt_update,
                            merchant_account.storage_scheme,
                        )
                        .await
                        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

                    payment_data.payment_intent = db
                        .update_payment_intent(
                            &state.into(),
                            payment_data.payment_intent,
                            payment_intent_update,
                            &key_store,
                            merchant_account.storage_scheme,
                        )
                        .await
                        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

                    let profile_id = payment_data
                        .payment_intent
                        .profile_id
                        .as_ref()
                        .get_required_value("profile_id")
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Could not find profile_id in payment intent")?;

                    let business_profile = db
                        .find_business_profile_by_profile_id(
                            key_manager_state,
                            &key_store,
                            profile_id,
                        )
                        .await
                        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
                            id: profile_id.get_string_repr().to_owned(),
                        })?;

                    // Trigger the outgoing webhook to notify the merchant about failed payment
                    let operation = operations::PaymentStatus;
                    Box::pin(utils::trigger_payments_webhook(
                        merchant_context,
                        business_profile,
                        payment_data,
                        customer,
                        state,
                        operation,
                    ))
                    .await
                    .map_err(|error| logger::warn!(payments_outgoing_webhook_error=?error))
                    .ok();
                }
            }
        };
        Ok(())
    }

    async fn error_handler<'a>(
        &'a self,
        state: &'a SessionState,
        process: storage::ProcessTracker,
        error: sch_errors::ProcessTrackerError,
    ) -> errors::CustomResult<(), sch_errors::ProcessTrackerError> {
        consumer::consumer_error_handler(state.store.as_scheduler(), process, error).await
    }
}

/// Get the next schedule time
///
/// The schedule time can be configured in configs by this key `pt_mapping_trustpay`
/// ```json
/// {
///     "default_mapping": {
///         "start_after": 60,
///         "frequency": [300],
///         "count": [5]
///     },
///     "max_retries_count": 5
/// }
/// ```
///
/// This config represents
///
/// `start_after`: The first psync should happen after 60 seconds
///
/// `frequency` and `count`: The next 5 retries should have an interval of 300 seconds between them
pub async fn get_sync_process_schedule_time(
    db: &dyn StorageInterface,
    connector: &str,
    merchant_id: &common_utils::id_type::MerchantId,
    retry_count: i32,
) -> Result<Option<time::PrimitiveDateTime>, errors::ProcessTrackerError> {
    let mapping: common_utils::errors::CustomResult<
        process_data::ConnectorPTMapping,
        errors::StorageError,
    > = db
        .find_config_by_key(&format!("pt_mapping_{connector}"))
        .await
        .map(|value| value.config)
        .and_then(|config| {
            config
                .parse_struct("ConnectorPTMapping")
                .change_context(errors::StorageError::DeserializationFailed)
        });
    let mapping = match mapping {
        Ok(x) => x,
        Err(error) => {
            logger::info!(?error, "Redis Mapping Error");
            process_data::ConnectorPTMapping::default()
        }
    };
    let time_delta = scheduler_utils::get_schedule_time(mapping, merchant_id, retry_count);

    Ok(scheduler_utils::get_time_from_delta(time_delta))
}

/// Schedule the task for retry
///
/// Returns bool which indicates whether this was the last retry or not
pub async fn retry_sync_task(
    db: &dyn StorageInterface,
    connector: String,
    merchant_id: common_utils::id_type::MerchantId,
    pt: storage::ProcessTracker,
) -> Result<bool, sch_errors::ProcessTrackerError> {
    let schedule_time =
        get_sync_process_schedule_time(db, &connector, &merchant_id, pt.retry_count + 1).await?;

    match schedule_time {
        Some(s_time) => {
            db.as_scheduler().retry_process(pt, s_time).await?;
            Ok(false)
        }
        None => {
            db.as_scheduler()
                .finish_process_with_business_status(pt, business_status::RETRIES_EXCEEDED)
                .await?;
            Ok(true)
        }
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::expect_used, clippy::unwrap_used)]
    use super::*;

    #[test]
    fn test_get_default_schedule_time() {
        let merchant_id =
            common_utils::id_type::MerchantId::try_from(std::borrow::Cow::from("-")).unwrap();
        let schedule_time_delta = scheduler_utils::get_schedule_time(
            process_data::ConnectorPTMapping::default(),
            &merchant_id,
            0,
        )
        .unwrap();
        let first_retry_time_delta = scheduler_utils::get_schedule_time(
            process_data::ConnectorPTMapping::default(),
            &merchant_id,
            1,
        )
        .unwrap();
        let cpt_default = process_data::ConnectorPTMapping::default().default_mapping;
        assert_eq!(
            vec![schedule_time_delta, first_retry_time_delta],
            vec![
                cpt_default.start_after,
                cpt_default.frequencies.first().unwrap().0
            ]
        );
    }
}


Documentation: api-reference/v2/business-profile/merchant-connector--list.mdx
# Type: Doc File

---
openapi: get /v2/profiles/{profile_id}/connector-accounts
---


pub struct Profile {
    pub profile_type: ProfileType,
    pub first_name: Secret<String>,
    pub last_name: Secret<String>,
    pub date_of_birth: Secret<String>,
    pub gender: NomupayGender,
    pub email_address: Email,
    pub phone_number_country_code: Option<String>,
    pub phone_number: Option<Secret<String>>,
    pub address: Address,
}


File: crates/router/src/services/kafka/fraud_check.rs
 Public functions: 1
 Public structs: 1

// use diesel_models::enums as storage_enums;
use diesel_models::{
    enums as storage_enums,
    enums::{FraudCheckLastStep, FraudCheckStatus, FraudCheckType},
    fraud_check::FraudCheck,
};
use time::OffsetDateTime;

#[derive(serde::Serialize, Debug)]
pub struct KafkaFraudCheck<'a> {
    pub frm_id: &'a String,
    pub payment_id: &'a common_utils::id_type::PaymentId,
    pub merchant_id: &'a common_utils::id_type::MerchantId,
    pub attempt_id: &'a String,
    #[serde(with = "time::serde::timestamp")]
    pub created_at: OffsetDateTime,
    pub frm_name: &'a String,
    pub frm_transaction_id: Option<&'a String>,
    pub frm_transaction_type: FraudCheckType,
    pub frm_status: FraudCheckStatus,
    pub frm_score: Option<i32>,
    pub frm_reason: Option<serde_json::Value>,
    pub frm_error: Option<&'a String>,
    pub payment_details: Option<serde_json::Value>,
    pub metadata: Option<serde_json::Value>,
    #[serde(with = "time::serde::timestamp")]
    pub modified_at: OffsetDateTime,
    pub last_step: FraudCheckLastStep,
    pub payment_capture_method: Option<storage_enums::CaptureMethod>, // In postFrm, we are updating capture method from automatic to manual. To store the merchant actual capture method, we are storing the actual capture method in payment_capture_method. It will be useful while approving the FRM decision.
}

impl<'a> KafkaFraudCheck<'a> {
    pub fn from_storage(check: &'a FraudCheck) -> Self {
        Self {
            frm_id: &check.frm_id,
            payment_id: &check.payment_id,
            merchant_id: &check.merchant_id,
            attempt_id: &check.attempt_id,
            created_at: check.created_at.assume_utc(),
            frm_name: &check.frm_name,
            frm_transaction_id: check.frm_transaction_id.as_ref(),
            frm_transaction_type: check.frm_transaction_type,
            frm_status: check.frm_status,
            frm_score: check.frm_score,
            frm_reason: check.frm_reason.clone(),
            frm_error: check.frm_error.as_ref(),
            payment_details: check.payment_details.clone(),
            metadata: check.metadata.clone(),
            modified_at: check.modified_at.assume_utc(),
            last_step: check.last_step,
            payment_capture_method: check.payment_capture_method,
        }
    }
}

impl super::KafkaMessage for KafkaFraudCheck<'_> {
    fn key(&self) -> String {
        format!(
            "{}_{}_{}_{}",
            self.merchant_id.get_string_repr(),
            self.payment_id.get_string_repr(),
            self.attempt_id,
            self.frm_id
        )
    }

    fn event_type(&self) -> crate::events::EventType {
        crate::events::EventType::FraudCheck
    }
}


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


pub struct RefundResponse {
    id: String,
    status: RefundStatus,
}


pub struct TsysTransactionDetails {
    #[serde(rename = "transactionID")]
    transaction_id: String,
    transaction_type: String,
    transaction_status: TsysTransactionStatus,
}


impl ConnectorErrorTypeMapping for Bluesnap {
    fn get_connector_error_type(
        &self,
        error_code: String,
        error_message: String,
    ) -> ConnectorErrorType {
        match (error_code.as_str(), error_message.as_str()) {
            ("7", "INVALID_TRANSACTION_TYPE") => ConnectorErrorType::UserError,
            ("30", "MISSING_SHOPPER_OR_CARD_HOLDER") => ConnectorErrorType::UserError,
            ("85", "INVALID_HTTP_METHOD") => ConnectorErrorType::BusinessError,
            ("90", "MISSING_CARD_TYPE") => ConnectorErrorType::BusinessError,
            ("10000", "INVALID_API_VERSION") => ConnectorErrorType::BusinessError,
            ("10000", "PAYMENT_GENERAL_FAILURE") => ConnectorErrorType::TechnicalError,
            ("10000", "SERVER_GENERAL_FAILURE") => ConnectorErrorType::BusinessError,
            ("10001", "VALIDATION_GENERAL_FAILURE") => ConnectorErrorType::BusinessError,
            ("10001", "INVALID_MERCHANT_TRANSACTION_ID") => ConnectorErrorType::BusinessError,
            ("10001", "INVALID_RECURRING_TRANSACTION") => ConnectorErrorType::BusinessError,
            ("10001", "MERCHANT_CONFIGURATION_ERROR") => ConnectorErrorType::BusinessError,
            ("10001", "MISSING_CARD_TYPE") => ConnectorErrorType::BusinessError,
            ("11001", "XSS_EXCEPTION") => ConnectorErrorType::UserError,
            ("14002", "THREE_D_SECURITY_AUTHENTICATION_REQUIRED") => {
                ConnectorErrorType::TechnicalError
            }
            ("14002", "ACCOUNT_CLOSED") => ConnectorErrorType::BusinessError,
            ("14002", "AUTHORIZATION_AMOUNT_ALREADY_REVERSED") => ConnectorErrorType::BusinessError,
            ("14002", "AUTHORIZATION_AMOUNT_NOT_VALID") => ConnectorErrorType::BusinessError,
            ("14002", "AUTHORIZATION_EXPIRED") => ConnectorErrorType::BusinessError,
            ("14002", "AUTHORIZATION_REVOKED") => ConnectorErrorType::BusinessError,
            ("14002", "AUTHORIZATION_NOT_FOUND") => ConnectorErrorType::UserError,
            ("14002", "BLS_CONNECTION_PROBLEM") => ConnectorErrorType::BusinessError,
            ("14002", "CALL_ISSUER") => ConnectorErrorType::UnknownError,
            ("14002", "CARD_LOST_OR_STOLEN") => ConnectorErrorType::UserError,
            ("14002", "CVV_ERROR") => ConnectorErrorType::UserError,
            ("14002", "DO_NOT_HONOR") => ConnectorErrorType::TechnicalError,
            ("14002", "EXPIRED_CARD") => ConnectorErrorType::UserError,
            ("14002", "GENERAL_PAYMENT_PROCESSING_ERROR") => ConnectorErrorType::TechnicalError,
            ("14002", "HIGH_RISK_ERROR") => ConnectorErrorType::BusinessError,
            ("14002", "INCORRECT_INFORMATION") => ConnectorErrorType::BusinessError,
            ("14002", "INCORRECT_SETUP") => ConnectorErrorType::BusinessError,
            ("14002", "INSUFFICIENT_FUNDS") => ConnectorErrorType::UserError,
            ("14002", "INVALID_AMOUNT") => ConnectorErrorType::BusinessError,
            ("14002", "INVALID_CARD_NUMBER") => ConnectorErrorType::UserError,
            ("14002", "INVALID_CARD_TYPE") => ConnectorErrorType::BusinessError,
            ("14002", "INVALID_PIN_OR_PW_OR_ID_ERROR") => ConnectorErrorType::UserError,
            ("14002", "INVALID_TRANSACTION") => ConnectorErrorType::BusinessError,
            ("14002", "LIMIT_EXCEEDED") => ConnectorErrorType::TechnicalError,
            ("14002", "PICKUP_CARD") => ConnectorErrorType::UserError,
            ("14002", "PROCESSING_AMOUNT_ERROR") => ConnectorErrorType::BusinessError,
            ("14002", "PROCESSING_DUPLICATE") => ConnectorErrorType::BusinessError,
            ("14002", "PROCESSING_GENERAL_DECLINE") => ConnectorErrorType::TechnicalError,
            ("14002", "PROCESSING_TIMEOUT") => ConnectorErrorType::TechnicalError,
            ("14002", "REFUND_FAILED") => ConnectorErrorType::TechnicalError,
            ("14002", "RESTRICTED_CARD") => ConnectorErrorType::UserError,
            ("14002", "STRONG_CUSTOMER_AUTHENTICATION_REQUIRED") => ConnectorErrorType::UserError,
            ("14002", "SYSTEM_TECHNICAL_ERROR") => ConnectorErrorType::BusinessError,
            ("14002", "THE_ISSUER_IS_UNAVAILABLE_OR_OFFLINE") => ConnectorErrorType::TechnicalError,
            ("14002", "THREE_D_SECURE_FAILURE") => ConnectorErrorType::UserError,
            ("14010", "FAILED_CREATING_PAYPAL_TOKEN") => ConnectorErrorType::TechnicalError,
            ("14011", "PAYMENT_METHOD_NOT_SUPPORTED") => ConnectorErrorType::BusinessError,
            ("14016", "NO_AVAILABLE_PROCESSORS") => ConnectorErrorType::TechnicalError,
            ("14034", "INVALID_PAYMENT_DETAILS") => ConnectorErrorType::UserError,
            ("15008", "SHOPPER_NOT_FOUND") => ConnectorErrorType::BusinessError,
            ("15012", "SHOPPER_COUNTRY_OFAC_SANCTIONED") => ConnectorErrorType::BusinessError,
            ("16003", "MULTIPLE_PAYMENT_METHODS_NON_SELECTED") => ConnectorErrorType::BusinessError,
            ("16001", "MISSING_ARGUMENTS") => ConnectorErrorType::BusinessError,
            ("17005", "INVALID_STEP_FIELD") => ConnectorErrorType::BusinessError,
            ("20002", "MULTIPLE_TRANSACTIONS_FOUND") => ConnectorErrorType::BusinessError,
            ("20003", "TRANSACTION_LOCKED") => ConnectorErrorType::BusinessError,
            ("20004", "TRANSACTION_PAYMENT_METHOD_NOT_SUPPORTED") => {
                ConnectorErrorType::BusinessError
            }
            ("20005", "TRANSACTION_NOT_AUTHORIZED") => ConnectorErrorType::UserError,
            ("20006", "TRANSACTION_ALREADY_EXISTS") => ConnectorErrorType::BusinessError,
            ("20007", "TRANSACTION_EXPIRED") => ConnectorErrorType::UserError,
            ("20008", "TRANSACTION_ID_REQUIRED") => ConnectorErrorType::TechnicalError,
            ("20009", "INVALID_TRANSACTION_ID") => ConnectorErrorType::BusinessError,
            ("20010", "TRANSACTION_ALREADY_CAPTURED") => ConnectorErrorType::BusinessError,
            ("20017", "TRANSACTION_CARD_NOT_VALID") => ConnectorErrorType::UserError,
            ("20031", "MISSING_RELEVANT_METHOD_FOR_SHOPPER") => ConnectorErrorType::BusinessError,
            ("20020", "INVALID_ALT_TRANSACTION_TYPE") => ConnectorErrorType::BusinessError,
            ("20021", "MULTI_SHOPPER_INFORMATION") => ConnectorErrorType::BusinessError,
            ("20022", "MISSING_SHOPPER_INFORMATION") => ConnectorErrorType::UserError,
            ("20023", "MISSING_PAYER_INFO_FIELDS") => ConnectorErrorType::UserError,
            ("20024", "EXPECT_NO_ECP_DETAILS") => ConnectorErrorType::UserError,
            ("20025", "INVALID_ECP_ACCOUNT_TYPE") => ConnectorErrorType::UserError,
            ("20025", "INVALID_PAYER_INFO_FIELDS") => ConnectorErrorType::UserError,
            ("20026", "MISMATCH_SUBSCRIPTION_CURRENCY") => ConnectorErrorType::BusinessError,
            ("20027", "PAYPAL_UNSUPPORTED_CURRENCY") => ConnectorErrorType::UserError,
            ("20033", "IDEAL_UNSUPPORTED_PAYMENT_INFO") => ConnectorErrorType::BusinessError,
            ("20035", "SOFORT_UNSUPPORTED_PAYMENT_INFO") => ConnectorErrorType::BusinessError,
            ("23001", "MISSING_WALLET_FIELDS") => ConnectorErrorType::BusinessError,
            ("23002", "INVALID_WALLET_FIELDS") => ConnectorErrorType::UserError,
            ("23003", "WALLET_PROCESSING_FAILURE") => ConnectorErrorType::TechnicalError,
            ("23005", "WALLET_EXPIRED") => ConnectorErrorType::UserError,
            ("23006", "WALLET_DUPLICATE_PAYMENT_METHODS") => ConnectorErrorType::BusinessError,
            ("23007", "WALLET_PAYMENT_NOT_ENABLED") => ConnectorErrorType::BusinessError,
            ("23008", "DUPLICATE_WALLET_RESOURCE") => ConnectorErrorType::BusinessError,
            ("23009", "WALLET_CLIENT_KEY_FAILURE") => ConnectorErrorType::BusinessError,
            ("23010", "INVALID_WALLET_PAYMENT_DATA") => ConnectorErrorType::UserError,
            ("23011", "WALLET_ONBOARDING_ERROR") => ConnectorErrorType::BusinessError,
            ("23012", "WALLET_MISSING_DOMAIN") => ConnectorErrorType::UserError,
            ("23013", "WALLET_UNREGISTERED_DOMAIN") => ConnectorErrorType::BusinessError,
            ("23014", "WALLET_CHECKOUT_CANCELED") => ConnectorErrorType::UserError,
            ("24012", "USER_NOT_AUTHORIZED") => ConnectorErrorType::UserError,
            ("24011", "CURRENCY_CODE_NOT_FOUND") => ConnectorErrorType::UserError,
            ("90009", "SUBSCRIPTION_NOT_FOUND") => ConnectorErrorType::UserError,
            (_, " MISSING_ARGUMENTS") => ConnectorErrorType::UnknownError,
            ("43008", "EXTERNAL_TAX_SERVICE_MISMATCH_CURRENCY") => {
                ConnectorErrorType::BusinessError
            }
            ("43009", "EXTERNAL_TAX_SERVICE_UNEXPECTED_TOTAL_PAYMENT") => {
                ConnectorErrorType::BusinessError
            }
            ("43010", "EXTERNAL_TAX_SERVICE_TAX_REFERENCE_ALREADY_USED") => {
                ConnectorErrorType::BusinessError
            }
            (
                _,
                "USER_NOT_AUTHORIZED"
                | "CREDIT_CARD_DETAILS_PLAIN_AND_ENCRYPTED"
                | "CREDIT_CARD_ENCRYPTED_SECURITY_CODE_REQUIRED"
                | "CREDIT_CARD_ENCRYPTED_NUMBER_REQUIRED",
            ) => ConnectorErrorType::UserError,
            _ => ConnectorErrorType::UnknownError,
        }
    }
}

impl ConnectorErrorTypeMapping for for Bluesnap


pub struct CybersourceRedirectionAuthResponse {
    pub transaction_id: String,
}


pub fn payments_list() {}