File: crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs
 Public structs: 21

use cards::CardNumber;
use common_enums::enums;
use common_utils::{pii::Email, request::Method, types::StringMajorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{BankDebitData, BankRedirectData, PaymentMethodData, WalletData},
    router_data::{ConnectorAuthType, PaymentMethodToken, RouterData},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types,
};
use hyperswitch_interfaces::errors;
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    unimplemented_payment_method,
    utils::{
        AddressDetailsData, BrowserInformationData, CardData as CardDataUtil,
        PaymentMethodTokenizationRequestData, PaymentsAuthorizeRequestData, RouterData as _,
    },
};

type Error = error_stack::Report<errors::ConnectorError>;

#[derive(Debug, Serialize)]
pub struct MollieRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for MollieRouterData<T> {
    fn from((amount, router_data): (StringMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data,
        }
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MolliePaymentsRequest {
    amount: Amount,
    description: String,
    redirect_url: String,
    cancel_url: Option<String>,
    webhook_url: String,
    locale: Option<String>,
    #[serde(flatten)]
    payment_method_data: MolliePaymentMethodData,
    metadata: Option<MollieMetadata>,
    sequence_type: SequenceType,
    mandate_id: Option<Secret<String>>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
pub struct Amount {
    currency: enums::Currency,
    value: StringMajorUnit,
}

#[derive(Debug, Serialize)]
#[serde(tag = "method")]
#[serde(rename_all = "lowercase")]
pub enum MolliePaymentMethodData {
    Applepay(Box<ApplePayMethodData>),
    Eps,
    Giropay,
    Ideal(Box<IdealMethodData>),
    Paypal(Box<PaypalMethodData>),
    Sofort,
    Przelewy24(Box<Przelewy24MethodData>),
    Bancontact,
    CreditCard(Box<CreditCardMethodData>),
    DirectDebit(Box<DirectDebitMethodData>),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayMethodData {
    apple_pay_payment_token: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IdealMethodData {
    issuer: Option<Secret<String>>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaypalMethodData {
    billing_address: Option<Address>,
    shipping_address: Option<Address>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Przelewy24MethodData {
    billing_email: Option<Email>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DirectDebitMethodData {
    consumer_name: Option<Secret<String>>,
    consumer_account: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreditCardMethodData {
    billing_address: Option<Address>,
    shipping_address: Option<Address>,
    card_token: Option<Secret<String>>,
}

#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SequenceType {
    #[default]
    Oneoff,
    First,
    Recurring,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Address {
    pub street_and_number: Secret<String>,
    pub postal_code: Secret<String>,
    pub city: String,
    pub region: Option<Secret<String>>,
    pub country: api_models::enums::CountryAlpha2,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MollieMetadata {
    pub order_id: String,
}

impl TryFrom<&MollieRouterData<&types::PaymentsAuthorizeRouterData>> for MolliePaymentsRequest {
    type Error = Error;
    fn try_from(
        item: &MollieRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let amount = Amount {
            currency: item.router_data.request.currency,
            value: item.amount.clone(),
        };
        let description = item.router_data.get_description()?;
        let redirect_url = item.router_data.request.get_router_return_url()?;
        let payment_method_data = match item.router_data.request.capture_method.unwrap_or_default()
        {
            enums::CaptureMethod::Automatic | enums::CaptureMethod::SequentialAutomatic => {
                match &item.router_data.request.payment_method_data {
                    PaymentMethodData::Card(_) => {
                        let pm_token = item.router_data.get_payment_method_token()?;
                        Ok(MolliePaymentMethodData::CreditCard(Box::new(
                            CreditCardMethodData {
                                billing_address: get_billing_details(item.router_data)?,
                                shipping_address: get_shipping_details(item.router_data)?,
                                card_token: Some(match pm_token {
                                    PaymentMethodToken::Token(token) => token,
                                    PaymentMethodToken::ApplePayDecrypt(_) => {
                                        Err(unimplemented_payment_method!(
                                            "Apple Pay",
                                            "Simplified",
                                            "Mollie"
                                        ))?
                                    }
                                    PaymentMethodToken::PazeDecrypt(_) => {
                                        Err(unimplemented_payment_method!("Paze", "Mollie"))?
                                    }
                                    PaymentMethodToken::GooglePayDecrypt(_) => {
                                        Err(unimplemented_payment_method!("Google Pay", "Mollie"))?
                                    }
                                }),
                            },
                        )))
                    }
                    PaymentMethodData::BankRedirect(ref redirect_data) => {
                        MolliePaymentMethodData::try_from((item.router_data, redirect_data))
                    }
                    PaymentMethodData::Wallet(ref wallet_data) => {
                        get_payment_method_for_wallet(item.router_data, wallet_data)
                    }
                    PaymentMethodData::BankDebit(ref directdebit_data) => {
                        MolliePaymentMethodData::try_from((directdebit_data, item.router_data))
                    }
                    _ => Err(
                        errors::ConnectorError::NotImplemented("Payment Method".to_string()).into(),
                    ),
                }
            }
            _ => Err(errors::ConnectorError::FlowNotSupported {
                flow: format!(
                    "{} capture",
                    item.router_data.request.capture_method.unwrap_or_default()
                ),
                connector: "Mollie".to_string(),
            }
            .into()),
        }?;
        Ok(Self {
            amount,
            description,
            redirect_url,
            cancel_url: None,
            /* webhook_url is a mandatory field.
            But we can't support webhook in our core hence keeping it as empty string */
            webhook_url: "".to_string(),
            locale: None,
            payment_method_data,
            metadata: Some(MollieMetadata {
                order_id: item.router_data.connector_request_reference_id.clone(),
            }),
            sequence_type: SequenceType::Oneoff,
            mandate_id: None,
        })
    }
}

impl TryFrom<(&types::PaymentsAuthorizeRouterData, &BankRedirectData)> for MolliePaymentMethodData {
    type Error = Error;
    fn try_from(
        (item, value): (&types::PaymentsAuthorizeRouterData, &BankRedirectData),
    ) -> Result<Self, Self::Error> {
        match value {
            BankRedirectData::Eps { .. } => Ok(Self::Eps),
            BankRedirectData::Giropay { .. } => Ok(Self::Giropay),
            BankRedirectData::Ideal { .. } => {
                Ok(Self::Ideal(Box::new(IdealMethodData {
                    // To do if possible this should be from the payment request
                    issuer: None,
                })))
            }
            BankRedirectData::Sofort { .. } => Ok(Self::Sofort),
            BankRedirectData::Przelewy24 { .. } => {
                Ok(Self::Przelewy24(Box::new(Przelewy24MethodData {
                    billing_email: item.get_optional_billing_email(),
                })))
            }
            BankRedirectData::BancontactCard { .. } => Ok(Self::Bancontact),
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

impl TryFrom<(&BankDebitData, &types::PaymentsAuthorizeRouterData)> for MolliePaymentMethodData {
    type Error = Error;
    fn try_from(
        (bank_debit_data, item): (&BankDebitData, &types::PaymentsAuthorizeRouterData),
    ) -> Result<Self, Self::Error> {
        match bank_debit_data {
            BankDebitData::SepaBankDebit { iban, .. } => {
                Ok(Self::DirectDebit(Box::new(DirectDebitMethodData {
                    consumer_name: item.get_optional_billing_full_name(),
                    consumer_account: iban.clone(),
                })))
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MollieCardTokenRequest {
    card_holder: Secret<String>,
    card_number: CardNumber,
    card_cvv: Secret<String>,
    card_expiry_date: Secret<String>,
    locale: String,
    testmode: bool,
    profile_token: Secret<String>,
}

impl TryFrom<&types::TokenizationRouterData> for MollieCardTokenRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::TokenizationRouterData) -> Result<Self, Self::Error> {
        match item.request.payment_method_data.clone() {
            PaymentMethodData::Card(ccard) => {
                let auth = MollieAuthType::try_from(&item.connector_auth_type)?;
                let card_holder = item
                    .get_optional_billing_full_name()
                    .unwrap_or(Secret::new("".to_string()));
                let card_number = ccard.card_number.clone();
                let card_expiry_date =
                    ccard.get_card_expiry_month_year_2_digit_with_delimiter("/".to_owned())?;
                let card_cvv = ccard.card_cvc;
                let locale = item.request.get_browser_info()?.get_language()?;
                let testmode =
                    item.test_mode
                        .ok_or(errors::ConnectorError::MissingRequiredField {
                            field_name: "test_mode",
                        })?;
                let profile_token = auth
                    .profile_token
                    .ok_or(errors::ConnectorError::FailedToObtainAuthType)?;
                Ok(Self {
                    card_holder,
                    card_number,
                    card_cvv,
                    card_expiry_date,
                    locale,
                    testmode,
                    profile_token,
                })
            }
            _ => Err(errors::ConnectorError::NotImplemented(
                "Payment Method".to_string(),
            ))?,
        }
    }
}

fn get_payment_method_for_wallet(
    item: &types::PaymentsAuthorizeRouterData,
    wallet_data: &WalletData,
) -> Result<MolliePaymentMethodData, Error> {
    match wallet_data {
        WalletData::PaypalRedirect { .. } => Ok(MolliePaymentMethodData::Paypal(Box::new(
            PaypalMethodData {
                billing_address: get_billing_details(item)?,
                shipping_address: get_shipping_details(item)?,
            },
        ))),
        WalletData::ApplePay(applepay_wallet_data) => {
            let apple_pay_encrypted_data = applepay_wallet_data
                .payment_data
                .get_encrypted_apple_pay_payment_data_mandatory()
                .change_context(errors::ConnectorError::MissingRequiredField {
                    field_name: "Apple pay encrypted data",
                })?;
            Ok(MolliePaymentMethodData::Applepay(Box::new(
                ApplePayMethodData {
                    apple_pay_payment_token: Secret::new(apple_pay_encrypted_data.to_owned()),
                },
            )))
        }
        _ => Err(errors::ConnectorError::NotImplemented("Payment Method".to_string()).into()),
    }
}

fn get_shipping_details(
    item: &types::PaymentsAuthorizeRouterData,
) -> Result<Option<Address>, Error> {
    let shipping_address = item
        .get_optional_shipping()
        .and_then(|shipping| shipping.address.as_ref());
    get_address_details(shipping_address)
}

fn get_billing_details(
    item: &types::PaymentsAuthorizeRouterData,
) -> Result<Option<Address>, Error> {
    let billing_address = item
        .get_optional_billing()
        .and_then(|billing| billing.address.as_ref());
    get_address_details(billing_address)
}

fn get_address_details(
    address: Option<&hyperswitch_domain_models::address::AddressDetails>,
) -> Result<Option<Address>, Error> {
    let address_details = match address {
        Some(address) => {
            let street_and_number = address.get_combined_address_line()?;
            let postal_code = address.get_zip()?.to_owned();
            let city = address.get_city()?.to_owned();
            let region = None;
            let country = address.get_country()?.to_owned();
            Some(Address {
                street_and_number,
                postal_code,
                city,
                region,
                country,
            })
        }
        None => None,
    };
    Ok(address_details)
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MolliePaymentsResponse {
    pub resource: String,
    pub id: String,
    pub amount: Amount,
    pub description: Option<String>,
    pub metadata: Option<MollieMetadata>,
    pub status: MolliePaymentStatus,
    pub is_cancelable: Option<bool>,
    pub sequence_type: SequenceType,
    pub redirect_url: Option<String>,
    pub webhook_url: Option<String>,
    #[serde(rename = "_links")]
    pub links: Links,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum MolliePaymentStatus {
    Open,
    Canceled,
    #[default]
    Pending,
    Authorized,
    Expired,
    Failed,
    Paid,
}

impl From<MolliePaymentStatus> for enums::AttemptStatus {
    fn from(item: MolliePaymentStatus) -> Self {
        match item {
            MolliePaymentStatus::Paid => Self::Charged,
            MolliePaymentStatus::Failed => Self::Failure,
            MolliePaymentStatus::Pending => Self::Pending,
            MolliePaymentStatus::Open => Self::AuthenticationPending,
            MolliePaymentStatus::Canceled => Self::Voided,
            MolliePaymentStatus::Authorized => Self::Authorized,
            MolliePaymentStatus::Expired => Self::Failure,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Link {
    href: Url,
    #[serde(rename = "type")]
    type_: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Links {
    #[serde(rename = "self")]
    self_: Option<Link>,
    checkout: Option<Link>,
    dashboard: Option<Link>,
    documentation: Option<Link>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CardDetails {
    pub card_number: Secret<String>,
    pub card_holder: Secret<String>,
    pub card_expiry_date: Secret<String>,
    pub card_cvv: Secret<String>,
}

pub struct MollieAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) profile_token: Option<Secret<String>>,
}

impl TryFrom<&ConnectorAuthType> for MollieAuthType {
    type Error = Error;
    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(),
                profile_token: None,
            }),
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                api_key: api_key.to_owned(),
                profile_token: Some(key1.to_owned()),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType)?,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MollieCardTokenResponse {
    card_token: Secret<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, MollieCardTokenResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, MollieCardTokenResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: enums::AttemptStatus::Pending,
            payment_method_token: Some(PaymentMethodToken::Token(item.response.card_token.clone())),
            response: Ok(PaymentsResponseData::TokenizationResponse {
                token: item.response.card_token.expose(),
            }),
            ..item.data
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, MolliePaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = Error;
    fn try_from(
        item: ResponseRouterData<F, MolliePaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let url = item
            .response
            .links
            .checkout
            .map(|link| RedirectForm::from((link.href, Method::Get)));
        Ok(Self {
            status: enums::AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()),
                redirection_data: Box::new(url),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(item.response.id),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

// REFUND :
#[derive(Default, Debug, Serialize)]
pub struct MollieRefundRequest {
    amount: Amount,
    description: Option<String>,
    metadata: Option<MollieMetadata>,
}

impl<F> TryFrom<&MollieRouterData<&types::RefundsRouterData<F>>> for MollieRefundRequest {
    type Error = Error;
    fn try_from(
        item: &MollieRouterData<&types::RefundsRouterData<F>>,
    ) -> Result<Self, Self::Error> {
        let amount = Amount {
            currency: item.router_data.request.currency,
            value: item.amount.clone(),
        };
        Ok(Self {
            amount,
            description: item.router_data.request.reason.to_owned(),
            metadata: Some(MollieMetadata {
                order_id: item.router_data.request.refund_id.clone(),
            }),
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RefundResponse {
    resource: String,
    id: String,
    amount: Amount,
    settlement_id: Option<String>,
    settlement_amount: Option<Amount>,
    status: MollieRefundStatus,
    description: Option<String>,
    metadata: Option<MollieMetadata>,
    payment_id: String,
    #[serde(rename = "_links")]
    links: Links,
}

#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MollieRefundStatus {
    Queued,
    #[default]
    Pending,
    Processing,
    Refunded,
    Failed,
    Canceled,
}

impl From<MollieRefundStatus> for enums::RefundStatus {
    fn from(item: MollieRefundStatus) -> Self {
        match item {
            MollieRefundStatus::Queued
            | MollieRefundStatus::Pending
            | MollieRefundStatus::Processing => Self::Pending,
            MollieRefundStatus::Refunded => Self::Success,
            MollieRefundStatus::Failed | MollieRefundStatus::Canceled => Self::Failure,
        }
    }
}

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

#[derive(Debug, Deserialize, Serialize)]
pub struct MollieErrorResponse {
    pub status: u16,
    pub title: Option<String>,
    pub detail: String,
    pub field: Option<String>,
}


pub struct RouterData<Flow, Request, Response> {
    pub flow: PhantomData<Flow>,
    pub merchant_id: id_type::MerchantId,
    pub customer_id: Option<id_type::CustomerId>,
    pub connector_customer: Option<String>,
    pub connector: String,
    // TODO: This should be a PaymentId type.
    // Make this change after all the connector dependency has been removed from connectors
    pub payment_id: String,
    pub attempt_id: String,
    pub tenant_id: id_type::TenantId,
    pub status: common_enums::enums::AttemptStatus,
    pub payment_method: common_enums::enums::PaymentMethod,
    pub connector_auth_type: ConnectorAuthType,
    pub description: Option<String>,
    pub address: PaymentAddress,
    pub auth_type: common_enums::enums::AuthenticationType,
    pub connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    pub connector_wallets_details: Option<common_utils::pii::SecretSerdeValue>,
    pub amount_captured: Option<i64>,
    pub access_token: Option<AccessToken>,
    pub session_token: Option<String>,
    pub reference_id: Option<String>,
    pub payment_method_token: Option<PaymentMethodToken>,
    pub recurring_mandate_payment_data: Option<RecurringMandatePaymentData>,
    pub preprocessing_id: Option<String>,
    /// This is the balance amount for gift cards or voucher
    pub payment_method_balance: Option<PaymentMethodBalance>,

    ///for switching between two different versions of the same connector
    pub connector_api_version: Option<String>,

    /// Contains flow-specific data required to construct a request and send it to the connector.
    pub request: Request,

    /// Contains flow-specific data that the connector responds with.
    pub response: Result<Response, ErrorResponse>,

    /// Contains a reference ID that should be sent in the connector request
    pub connector_request_reference_id: String,

    #[cfg(feature = "payouts")]
    /// Contains payout method data
    pub payout_method_data: Option<api_models::payouts::PayoutMethodData>,

    #[cfg(feature = "payouts")]
    /// Contains payout's quote ID
    pub quote_id: Option<String>,

    pub test_mode: Option<bool>,
    pub connector_http_status_code: Option<u16>,
    pub external_latency: Option<u128>,
    /// Contains apple pay flow type simplified or manual
    pub apple_pay_flow: Option<payment_method_data::ApplePayFlow>,

    pub frm_metadata: Option<common_utils::pii::SecretSerdeValue>,

    pub dispute_id: Option<String>,
    pub refund_id: Option<String>,

    /// This field is used to store various data regarding the response from connector
    pub connector_response: Option<ConnectorResponseData>,
    pub payment_method_status: Option<common_enums::PaymentMethodStatus>,

    // minor amount for amount framework
    pub minor_amount_captured: Option<MinorUnit>,
    pub minor_amount_capturable: Option<MinorUnit>,

    pub integrity_check: Result<(), IntegrityCheckError>,

    pub additional_merchant_data: Option<api_models::admin::AdditionalMerchantData>,

    pub header_payload: Option<payments::HeaderPayload>,

    pub connector_mandate_request_reference_id: Option<String>,

    pub l2_l3_data: Option<L2L3Data>,

    pub authentication_id: Option<id_type::AuthenticationId>,
    /// Contains the type of sca exemption required for the transaction
    pub psd2_sca_exemption_type: Option<common_enums::ScaExemptionType>,

    /// Contains stringified connector raw response body
    pub raw_connector_response: Option<Secret<String>>,

    /// Indicates whether the payment ID was provided by the merchant (true),
    /// or generated internally by Hyperswitch (false)
    pub is_payment_id_from_merchant: Option<bool>,
}


impl ExternalAuthentication for Threedsecureio {}

impl ExternalAuthentication for for Threedsecureio


File: crates/hyperswitch_connectors/src/connectors/barclaycard/transformers.rs
 Public functions: 1
 Public structs: 71

use base64::Engine;
use common_enums::enums;
use common_utils::{
    consts, date_time,
    ext_traits::ValueExt,
    pii,
    types::{SemanticVersion, StringMajorUnit},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{GooglePayWalletData, PaymentMethodData, WalletData},
    router_data::{
        AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, ConnectorResponseData,
        ErrorResponse, RouterData,
    },
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::{
        authentication::MessageExtensionAttribute, CompleteAuthorizeData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsPreProcessingData, PaymentsSyncData,
        ResponseId,
    },
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::errors;
use masking::{ExposeInterface, PeekInterface, Secret};
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::{
    constants,
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{
        self, AddressDetailsData, CardData, PaymentsAuthorizeRequestData,
        PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingRequestData,
        PaymentsSyncRequestData, RouterData as OtherRouterData,
    },
};
pub struct BarclaycardAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) merchant_account: Secret<String>,
    pub(super) api_secret: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for BarclaycardAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        if let ConnectorAuthType::SignatureKey {
            api_key,
            key1,
            api_secret,
        } = auth_type
        {
            Ok(Self {
                api_key: api_key.to_owned(),
                merchant_account: key1.to_owned(),
                api_secret: api_secret.to_owned(),
            })
        } else {
            Err(errors::ConnectorError::FailedToObtainAuthType)?
        }
    }
}

pub struct BarclaycardRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

impl<T> TryFrom<(StringMajorUnit, T)> for BarclaycardRouterData<T> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from((amount, item): (StringMajorUnit, T)) -> Result<Self, Self::Error> {
        Ok(Self {
            amount,
            router_data: item,
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardPaymentsRequest {
    processing_information: ProcessingInformation,
    payment_information: PaymentInformation,
    order_information: OrderInformationWithBill,
    client_reference_information: ClientReferenceInformation,
    #[serde(skip_serializing_if = "Option::is_none")]
    consumer_authentication_information: Option<BarclaycardConsumerAuthInformation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    merchant_defined_information: Option<Vec<MerchantDefinedInformation>>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProcessingInformation {
    commerce_indicator: String,
    capture: Option<bool>,
    payment_solution: Option<String>,
    cavv_algorithm: Option<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MerchantDefinedInformation {
    key: u8,
    value: String,
}

#[derive(Debug, Serialize)]
pub enum BarclaycardParesStatus {
    #[serde(rename = "Y")]
    AuthenticationSuccessful,
    #[serde(rename = "A")]
    AuthenticationAttempted,
    #[serde(rename = "N")]
    AuthenticationFailed,
    #[serde(rename = "U")]
    AuthenticationNotCompleted,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardConsumerAuthInformation {
    ucaf_collection_indicator: Option<String>,
    cavv: Option<Secret<String>>,
    ucaf_authentication_data: Option<Secret<String>>,
    xid: Option<String>,
    directory_server_transaction_id: Option<Secret<String>>,
    specification_version: Option<SemanticVersion>,
    /// This field specifies the 3ds version
    pa_specification_version: Option<SemanticVersion>,
    /// Verification response enrollment status.
    ///
    /// This field is supported only on Asia, Middle East, and Africa Gateway.
    ///
    /// For external authentication, this field will always be "Y"
    veres_enrolled: Option<String>,
    /// Raw electronic commerce indicator (ECI)
    eci_raw: Option<String>,
    /// This field is supported only on Asia, Middle East, and Africa Gateway
    /// Also needed for Credit Mutuel-CIC in France and Mastercard Identity Check transactions
    /// This field is only applicable for Mastercard and Visa Transactions
    pares_status: Option<BarclaycardParesStatus>,
    //This field is used to send the authentication date in yyyyMMDDHHMMSS format
    authentication_date: Option<String>,
    /// This field indicates the 3D Secure transaction flow. It is only supported for secure transactions in France.
    /// The possible values are - CH (Challenge), FD (Frictionless with delegation), FR (Frictionless)
    effective_authentication_type: Option<EffectiveAuthenticationType>,
    /// This field indicates the authentication type or challenge presented to the cardholder at checkout.
    challenge_code: Option<String>,
    /// This field indicates the reason for payer authentication response status. It is only supported for secure transactions in France.
    pares_status_reason: Option<String>,
    /// This field indicates the reason why strong authentication was cancelled. It is only supported for secure transactions in France.
    challenge_cancel_code: Option<String>,
    /// This field indicates the score calculated by the 3D Securing platform. It is only supported for secure transactions in France.
    network_score: Option<u32>,
    /// This is the transaction ID generated by the access control server. This field is supported only for secure transactions in France.
    acs_transaction_id: Option<String>,
}

#[derive(Debug, Serialize)]
pub enum EffectiveAuthenticationType {
    CH,
    FR,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CardPaymentInformation {
    card: Card,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayPaymentInformation {
    fluid_data: FluidData,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum PaymentInformation {
    Cards(Box<CardPaymentInformation>),
    GooglePay(Box<GooglePayPaymentInformation>),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Card {
    number: cards::CardNumber,
    expiration_month: Secret<String>,
    expiration_year: Secret<String>,
    security_code: Secret<String>,
    #[serde(rename = "type")]
    card_type: Option<String>,
    type_selection_indicator: Option<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FluidData {
    value: Secret<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderInformationWithBill {
    amount_details: Amount,
    bill_to: Option<BillTo>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Amount {
    total_amount: StringMajorUnit,
    currency: api_models::enums::Currency,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BillTo {
    first_name: Secret<String>,
    last_name: Secret<String>,
    address1: Secret<String>,
    locality: String,
    administrative_area: Secret<String>,
    postal_code: Secret<String>,
    country: enums::CountryAlpha2,
    email: pii::Email,
}

fn truncate_string(state: &Secret<String>, max_len: usize) -> Secret<String> {
    let exposed = state.clone().expose();
    let truncated = exposed.get(..max_len).unwrap_or(&exposed);
    Secret::new(truncated.to_string())
}

fn build_bill_to(
    address_details: &hyperswitch_domain_models::address::AddressDetails,
    email: pii::Email,
) -> Result<BillTo, error_stack::Report<errors::ConnectorError>> {
    let administrative_area = address_details
        .to_state_code_as_optional()
        .unwrap_or_else(|_| {
            address_details
                .get_state()
                .ok()
                .map(|state| truncate_string(state, 20))
        })
        .ok_or_else(|| errors::ConnectorError::MissingRequiredField {
            field_name: "billing_address.state",
        })?;

    Ok(BillTo {
        first_name: address_details.get_first_name()?.clone(),
        last_name: address_details.get_last_name()?.clone(),
        address1: address_details.get_line1()?.clone(),
        locality: address_details.get_city()?.clone(),
        administrative_area,
        postal_code: address_details.get_zip()?.clone(),
        country: address_details.get_country()?.to_owned(),
        email,
    })
}

fn get_barclaycard_card_type(card_network: common_enums::CardNetwork) -> Option<&'static str> {
    match card_network {
        common_enums::CardNetwork::Visa => Some("001"),
        common_enums::CardNetwork::Mastercard => Some("002"),
        common_enums::CardNetwork::AmericanExpress => Some("003"),
        common_enums::CardNetwork::JCB => Some("007"),
        common_enums::CardNetwork::DinersClub => Some("005"),
        common_enums::CardNetwork::Discover => Some("004"),
        common_enums::CardNetwork::CartesBancaires => Some("006"),
        common_enums::CardNetwork::UnionPay => Some("062"),
        //"042" is the type code for Masetro Cards(International). For Maestro Cards(UK-Domestic) the mapping should be "024"
        common_enums::CardNetwork::Maestro => Some("042"),
        common_enums::CardNetwork::Interac
        | common_enums::CardNetwork::RuPay
        | common_enums::CardNetwork::Star
        | common_enums::CardNetwork::Accel
        | common_enums::CardNetwork::Pulse
        | common_enums::CardNetwork::Nyce => None,
    }
}

#[derive(Debug, Serialize)]
pub enum PaymentSolution {
    GooglePay,
}

impl From<PaymentSolution> for String {
    fn from(solution: PaymentSolution) -> Self {
        let payment_solution = match solution {
            PaymentSolution::GooglePay => "012",
        };
        payment_solution.to_string()
    }
}

impl
    From<(
        &BarclaycardRouterData<&PaymentsAuthorizeRouterData>,
        Option<BillTo>,
    )> for OrderInformationWithBill
{
    fn from(
        (item, bill_to): (
            &BarclaycardRouterData<&PaymentsAuthorizeRouterData>,
            Option<BillTo>,
        ),
    ) -> Self {
        Self {
            amount_details: Amount {
                total_amount: item.amount.clone(),
                currency: item.router_data.request.currency,
            },
            bill_to,
        }
    }
}

impl
    TryFrom<(
        &BarclaycardRouterData<&PaymentsAuthorizeRouterData>,
        Option<PaymentSolution>,
        Option<String>,
    )> for ProcessingInformation
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        (item, solution, network): (
            &BarclaycardRouterData<&PaymentsAuthorizeRouterData>,
            Option<PaymentSolution>,
            Option<String>,
        ),
    ) -> Result<Self, Self::Error> {
        let commerce_indicator = get_commerce_indicator(network);
        let cavv_algorithm = Some("2".to_string());
        Ok(Self {
            capture: Some(matches!(
                item.router_data.request.capture_method,
                Some(enums::CaptureMethod::Automatic) | None
            )),
            payment_solution: solution.map(String::from),
            commerce_indicator,
            cavv_algorithm,
        })
    }
}

impl
    TryFrom<(
        &BarclaycardRouterData<&PaymentsCompleteAuthorizeRouterData>,
        Option<PaymentSolution>,
        Option<String>,
    )> for ProcessingInformation
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        (item, solution, network): (
            &BarclaycardRouterData<&PaymentsCompleteAuthorizeRouterData>,
            Option<PaymentSolution>,
            Option<String>,
        ),
    ) -> Result<Self, Self::Error> {
        let commerce_indicator = get_commerce_indicator(network);
        let cavv_algorithm = Some("2".to_string());
        Ok(Self {
            capture: Some(matches!(
                item.router_data.request.capture_method,
                Some(enums::CaptureMethod::Automatic) | None
            )),
            payment_solution: solution.map(String::from),
            commerce_indicator,
            cavv_algorithm,
        })
    }
}

impl From<&BarclaycardRouterData<&PaymentsCompleteAuthorizeRouterData>>
    for ClientReferenceInformation
{
    fn from(item: &BarclaycardRouterData<&PaymentsCompleteAuthorizeRouterData>) -> Self {
        Self {
            code: Some(item.router_data.connector_request_reference_id.clone()),
        }
    }
}

impl From<&BarclaycardRouterData<&PaymentsAuthorizeRouterData>> for ClientReferenceInformation {
    fn from(item: &BarclaycardRouterData<&PaymentsAuthorizeRouterData>) -> Self {
        Self {
            code: Some(item.router_data.connector_request_reference_id.clone()),
        }
    }
}

impl
    From<(
        &BarclaycardRouterData<&PaymentsCompleteAuthorizeRouterData>,
        BillTo,
    )> for OrderInformationWithBill
{
    fn from(
        (item, bill_to): (
            &BarclaycardRouterData<&PaymentsCompleteAuthorizeRouterData>,
            BillTo,
        ),
    ) -> Self {
        Self {
            amount_details: Amount {
                total_amount: item.amount.clone(),
                currency: item.router_data.request.currency,
            },
            bill_to: Some(bill_to),
        }
    }
}

impl From<BarclaycardAuthEnrollmentStatus> for enums::AttemptStatus {
    fn from(item: BarclaycardAuthEnrollmentStatus) -> Self {
        match item {
            BarclaycardAuthEnrollmentStatus::PendingAuthentication => Self::AuthenticationPending,
            BarclaycardAuthEnrollmentStatus::AuthenticationSuccessful => {
                Self::AuthenticationSuccessful
            }
            BarclaycardAuthEnrollmentStatus::AuthenticationFailed => Self::AuthenticationFailed,
        }
    }
}

impl From<common_enums::DecoupledAuthenticationType> for EffectiveAuthenticationType {
    fn from(auth_type: common_enums::DecoupledAuthenticationType) -> Self {
        match auth_type {
            common_enums::DecoupledAuthenticationType::Challenge => Self::CH,
            common_enums::DecoupledAuthenticationType::Frictionless => Self::FR,
        }
    }
}

fn convert_metadata_to_merchant_defined_info(metadata: Value) -> Vec<MerchantDefinedInformation> {
    let hashmap: std::collections::BTreeMap<String, Value> =
        serde_json::from_str(&metadata.to_string()).unwrap_or(std::collections::BTreeMap::new());
    let mut vector = Vec::new();
    let mut iter = 1;
    for (key, value) in hashmap {
        vector.push(MerchantDefinedInformation {
            key: iter,
            value: format!("{key}={value}"),
        });
        iter += 1;
    }
    vector
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientReferenceInformation {
    code: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientProcessorInformation {
    avs: Option<Avs>,
    card_verification: Option<CardVerification>,
    processor: Option<ProcessorResponse>,
    network_transaction_id: Option<Secret<String>>,
    approval_code: Option<String>,
    merchant_advice: Option<MerchantAdvice>,
    response_code: Option<String>,
    ach_verification: Option<AchVerification>,
    system_trace_audit_number: Option<String>,
    event_status: Option<String>,
    retrieval_reference_number: Option<String>,
    consumer_authentication_response: Option<ConsumerAuthenticationResponse>,
    response_details: Option<String>,
    transaction_id: Option<Secret<String>>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MerchantAdvice {
    code: Option<String>,
    code_raw: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ConsumerAuthenticationResponse {
    code: Option<String>,
    code_raw: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AchVerification {
    result_code_raw: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProcessorResponse {
    name: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CardVerification {
    result_code: Option<String>,
    result_code_raw: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientRiskInformation {
    rules: Option<Vec<ClientRiskInformationRules>>,
    profile: Option<Profile>,
    score: Option<Score>,
    info_codes: Option<InfoCodes>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InfoCodes {
    address: Option<Vec<String>>,
    identity_change: Option<Vec<String>>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Score {
    factor_codes: Option<Vec<String>>,
    result: Option<RiskResult>,
    model_used: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum RiskResult {
    StringVariant(String),
    IntVariant(u64),
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Profile {
    early_decision: Option<String>,
    name: Option<String>,
    decision: Option<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ClientRiskInformationRules {
    name: Option<Secret<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Avs {
    code: Option<String>,
    code_raw: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardConsumerAuthValidateResponse {
    ucaf_collection_indicator: Option<String>,
    cavv: Option<Secret<String>>,
    ucaf_authentication_data: Option<Secret<String>>,
    xid: Option<String>,
    specification_version: Option<SemanticVersion>,
    directory_server_transaction_id: Option<Secret<String>>,
    indicator: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct BarclaycardThreeDSMetadata {
    three_ds_data: BarclaycardConsumerAuthValidateResponse,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardConsumerAuthInformationEnrollmentResponse {
    access_token: Option<Secret<String>>,
    step_up_url: Option<String>,
    //Added to segregate the three_ds_data in a separate struct
    #[serde(flatten)]
    validate_response: BarclaycardConsumerAuthValidateResponse,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BarclaycardAuthEnrollmentStatus {
    PendingAuthentication,
    AuthenticationSuccessful,
    AuthenticationFailed,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientAuthCheckInfoResponse {
    id: String,
    client_reference_information: ClientReferenceInformation,
    consumer_authentication_information: BarclaycardConsumerAuthInformationEnrollmentResponse,
    status: BarclaycardAuthEnrollmentStatus,
    error_information: Option<BarclaycardErrorInformation>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardConsumerAuthInformationValidateRequest {
    authentication_transaction_id: String,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BarclaycardPreProcessingResponse {
    ClientAuthCheckInfo(Box<ClientAuthCheckInfoResponse>),
    ErrorInformation(Box<BarclaycardErrorInformationResponse>),
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardAuthSetupRequest {
    payment_information: PaymentInformation,
    client_reference_information: ClientReferenceInformation,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardAuthValidateRequest {
    payment_information: PaymentInformation,
    client_reference_information: ClientReferenceInformation,
    consumer_authentication_information: BarclaycardConsumerAuthInformationValidateRequest,
    order_information: OrderInformation,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardAuthEnrollmentRequest {
    payment_information: PaymentInformation,
    client_reference_information: ClientReferenceInformation,
    consumer_authentication_information: BarclaycardConsumerAuthInformationRequest,
    order_information: OrderInformationWithBill,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct BarclaycardRedirectionAuthResponse {
    pub transaction_id: String,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardConsumerAuthInformationRequest {
    return_url: String,
    reference_id: String,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum BarclaycardPreProcessingRequest {
    AuthEnrollment(Box<BarclaycardAuthEnrollmentRequest>),
    AuthValidate(Box<BarclaycardAuthValidateRequest>),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BarclaycardConsumerAuthInformationResponse {
    access_token: String,
    device_data_collection_url: String,
    reference_id: String,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientAuthSetupInfoResponse {
    id: String,
    client_reference_information: ClientReferenceInformation,
    consumer_authentication_information: BarclaycardConsumerAuthInformationResponse,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BarclaycardAuthSetupResponse {
    ClientAuthSetupInfo(Box<ClientAuthSetupInfoResponse>),
    ErrorInformation(Box<BarclaycardErrorInformationResponse>),
}

impl TryFrom<&BarclaycardRouterData<&PaymentsPreProcessingRouterData>>
    for BarclaycardPreProcessingRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &BarclaycardRouterData<&PaymentsPreProcessingRouterData>,
    ) -> Result<Self, Self::Error> {
        let client_reference_information = ClientReferenceInformation {
            code: Some(item.router_data.connector_request_reference_id.clone()),
        };
        let payment_method_data = item.router_data.request.payment_method_data.clone().ok_or(
            errors::ConnectorError::MissingConnectorRedirectionPayload {
                field_name: "payment_method_data",
            },
        )?;
        let payment_information = match payment_method_data {
            PaymentMethodData::Card(ccard) => {
                let card_type = match ccard
                    .card_network
                    .clone()
                    .and_then(get_barclaycard_card_type)
                {
                    Some(card_network) => Some(card_network.to_string()),
                    None => ccard.get_card_issuer().ok().map(String::from),
                };

                Ok(PaymentInformation::Cards(Box::new(
                    CardPaymentInformation {
                        card: Card {
                            number: ccard.card_number,
                            expiration_month: ccard.card_exp_month,
                            expiration_year: ccard.card_exp_year,
                            security_code: ccard.card_cvc,
                            card_type,
                            type_selection_indicator: Some("1".to_owned()),
                        },
                    },
                )))
            }
            PaymentMethodData::Wallet(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("Barclaycard"),
                ))
            }
        }?;

        let redirect_response = item.router_data.request.redirect_response.clone().ok_or(
            errors::ConnectorError::MissingRequiredField {
                field_name: "redirect_response",
            },
        )?;

        let amount_details = Amount {
            total_amount: item.amount.clone(),
            currency: item.router_data.request.currency.ok_or(
                errors::ConnectorError::MissingRequiredField {
                    field_name: "currency",
                },
            )?,
        };

        match redirect_response.params {
            Some(param) if !param.clone().peek().is_empty() => {
                let reference_id = param
                    .clone()
                    .peek()
                    .split_once('=')
                    .ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload {
                        field_name: "request.redirect_response.params.reference_id",
                    })?
                    .1
                    .to_string();
                let email = item
                    .router_data
                    .get_billing_email()
                    .or(item.router_data.request.get_email())?;
                let bill_to = build_bill_to(item.router_data.get_billing_address()?, email)?;
                let order_information = OrderInformationWithBill {
                    amount_details,
                    bill_to: Some(bill_to),
                };
                Ok(Self::AuthEnrollment(Box::new(
                    BarclaycardAuthEnrollmentRequest {
                        payment_information,
                        client_reference_information,
                        consumer_authentication_information:
                            BarclaycardConsumerAuthInformationRequest {
                                return_url: item
                                    .router_data
                                    .request
                                    .get_complete_authorize_url()?,
                                reference_id,
                            },
                        order_information,
                    },
                )))
            }
            Some(_) | None => {
                let redirect_payload: BarclaycardRedirectionAuthResponse = redirect_response
                    .payload
                    .ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload {
                        field_name: "request.redirect_response.payload",
                    })?
                    .peek()
                    .clone()
                    .parse_value("BarclaycardRedirectionAuthResponse")
                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
                let order_information = OrderInformation { amount_details };
                Ok(Self::AuthValidate(Box::new(
                    BarclaycardAuthValidateRequest {
                        payment_information,
                        client_reference_information,
                        consumer_authentication_information:
                            BarclaycardConsumerAuthInformationValidateRequest {
                                authentication_transaction_id: redirect_payload.transaction_id,
                            },
                        order_information,
                    },
                )))
            }
        }
    }
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            BarclaycardPreProcessingResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsPreProcessingData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            BarclaycardPreProcessingResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        match item.response {
            BarclaycardPreProcessingResponse::ClientAuthCheckInfo(info_response) => {
                let status = enums::AttemptStatus::from(info_response.status);
                let risk_info: Option<ClientRiskInformation> = None;
                if utils::is_payment_failure(status) {
                    let response = Err(get_error_response(
                        &info_response.error_information,
                        &None,
                        &risk_info,
                        Some(status),
                        item.http_code,
                        info_response.id.clone(),
                    ));

                    Ok(Self {
                        status,
                        response,
                        ..item.data
                    })
                } else {
                    let connector_response_reference_id = Some(
                        info_response
                            .client_reference_information
                            .code
                            .unwrap_or(info_response.id.clone()),
                    );

                    let redirection_data = match (
                        info_response
                            .consumer_authentication_information
                            .access_token,
                        info_response
                            .consumer_authentication_information
                            .step_up_url,
                    ) {
                        (Some(token), Some(step_up_url)) => {
                            Some(RedirectForm::BarclaycardConsumerAuth {
                                access_token: token.expose(),
                                step_up_url,
                            })
                        }
                        _ => None,
                    };
                    let three_ds_data = serde_json::to_value(
                        info_response
                            .consumer_authentication_information
                            .validate_response,
                    )
                    .change_context(errors::ConnectorError::ResponseHandlingFailed)?;
                    Ok(Self {
                        status,
                        response: Ok(PaymentsResponseData::TransactionResponse {
                            resource_id: ResponseId::NoResponseId,
                            redirection_data: Box::new(redirection_data),
                            mandate_reference: Box::new(None),
                            connector_metadata: Some(serde_json::json!({
                                "three_ds_data": three_ds_data
                            })),
                            network_txn_id: None,
                            connector_response_reference_id,
                            incremental_authorization_allowed: None,
                            charges: None,
                        }),
                        ..item.data
                    })
                }
            }
            BarclaycardPreProcessingResponse::ErrorInformation(error_response) => {
                let detailed_error_info =
                    error_response
                        .error_information
                        .details
                        .to_owned()
                        .map(|details| {
                            details
                                .iter()
                                .map(|details| format!("{} : {}", details.field, details.reason))
                                .collect::<Vec<_>>()
                                .join(", ")
                        });

                let reason = get_error_reason(
                    error_response.error_information.message,
                    detailed_error_info,
                    None,
                );
                let error_message = error_response.error_information.reason.to_owned();
                let response = Err(ErrorResponse {
                    code: error_message
                        .clone()
                        .unwrap_or(hyperswitch_interfaces::consts::NO_ERROR_CODE.to_string()),
                    message: error_message
                        .unwrap_or(hyperswitch_interfaces::consts::NO_ERROR_MESSAGE.to_string()),
                    reason,
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: Some(error_response.id.clone()),
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                });
                Ok(Self {
                    response,
                    status: enums::AttemptStatus::AuthenticationFailed,
                    ..item.data
                })
            }
        }
    }
}

fn extract_score_id(message_extensions: &[MessageExtensionAttribute]) -> Option<u32> {
    message_extensions.iter().find_map(|attr| {
        attr.id
            .ends_with("CB-SCORE")
            .then(|| {
                attr.id
                    .split('_')
                    .next()
                    .and_then(|p| p.strip_prefix('A'))
                    .and_then(|s| {
                        s.parse::<u32>().map(Some).unwrap_or_else(|err| {
                            router_env::logger::error!(
                                "Failed to parse score_id from '{}': {}",
                                s,
                                err
                            );
                            None
                        })
                    })
                    .or_else(|| {
                        router_env::logger::error!("Unexpected prefix format in id: {}", attr.id);
                        None
                    })
            })
            .flatten()
    })
}

impl TryFrom<&BarclaycardRouterData<&PaymentsAuthorizeRouterData>> for BarclaycardAuthSetupRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &BarclaycardRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(ccard) => {
                let card_type = match ccard
                    .card_network
                    .clone()
                    .and_then(get_barclaycard_card_type)
                {
                    Some(card_network) => Some(card_network.to_string()),
                    None => ccard.get_card_issuer().ok().map(String::from),
                };

                let payment_information =
                    PaymentInformation::Cards(Box::new(CardPaymentInformation {
                        card: Card {
                            number: ccard.card_number,
                            expiration_month: ccard.card_exp_month,
                            expiration_year: ccard.card_exp_year,
                            security_code: ccard.card_cvc,
                            card_type,
                            type_selection_indicator: Some("1".to_owned()),
                        },
                    }));
                let client_reference_information = ClientReferenceInformation::from(item);
                Ok(Self {
                    payment_information,
                    client_reference_information,
                })
            }
            PaymentMethodData::Wallet(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    utils::get_unimplemented_payment_method_error_message("Barclaycard"),
                )
                .into())
            }
        }
    }
}

impl
    TryFrom<(
        &BarclaycardRouterData<&PaymentsAuthorizeRouterData>,
        hyperswitch_domain_models::payment_method_data::Card,
    )> for BarclaycardPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        (item, ccard): (
            &BarclaycardRouterData<&PaymentsAuthorizeRouterData>,
            hyperswitch_domain_models::payment_method_data::Card,
        ),
    ) -> Result<Self, Self::Error> {
        let email = item
            .router_data
            .get_billing_email()
            .or(item.router_data.request.get_email())?;
        let bill_to = build_bill_to(item.router_data.get_billing_address()?, email)?;
        let order_information = OrderInformationWithBill::from((item, Some(bill_to)));
        let payment_information = PaymentInformation::try_from(&ccard)?;
        let processing_information = ProcessingInformation::try_from((item, None, None))?;
        let client_reference_information = ClientReferenceInformation::from(item);
        let merchant_defined_information = item
            .router_data
            .request
            .metadata
            .clone()
            .map(convert_metadata_to_merchant_defined_info);

        let pares_status = Some(BarclaycardParesStatus::AuthenticationSuccessful);

        let consumer_authentication_information = item
            .router_data
            .request
            .authentication_data
            .as_ref()
            .map(|authn_data| {
                let (ucaf_authentication_data, cavv, ucaf_collection_indicator) =
                    if ccard.card_network == Some(common_enums::CardNetwork::Mastercard) {
                        (Some(authn_data.cavv.clone()), None, Some("2".to_string()))
                    } else {
                        (None, Some(authn_data.cavv.clone()), None)
                    };
                let authentication_date = date_time::format_date(
                    authn_data.created_at,
                    date_time::DateFormat::YYYYMMDDHHmmss,
                )
                .ok();
                let effective_authentication_type = authn_data.authentication_type.map(Into::into);
                let network_score: Option<u32> =
                    if ccard.card_network == Some(common_enums::CardNetwork::CartesBancaires) {
                        match authn_data.message_extension.as_ref() {
                            Some(secret) => {
                                let exposed_value = secret.clone().expose();
                                match serde_json::from_value::<Vec<MessageExtensionAttribute>>(
                                    exposed_value,
                                ) {
                                    Ok(exts) => extract_score_id(&exts),
                                    Err(err) => {
                                        router_env::logger::error!(
                                            "Failed to deserialize message_extension: {:?}",
                                            err
                                        );
                                        None
                                    }
                                }
                            }
                            None => None,
                        }
                    } else {
                        None
                    };
                BarclaycardConsumerAuthInformation {
                    pares_status,
                    ucaf_collection_indicator,
                    cavv,
                    ucaf_authentication_data,
                    xid: None,
                    directory_server_transaction_id: authn_data
                        .ds_trans_id
                        .clone()
                        .map(Secret::new),
                    specification_version: authn_data.message_version.clone(),
                    pa_specification_version: authn_data.message_version.clone(),
                    veres_enrolled: Some("Y".to_string()),
                    eci_raw: authn_data.eci.clone(),
                    authentication_date,
                    effective_authentication_type,
                    challenge_code: authn_data.challenge_code.clone(),


pub async fn payouts_list_by_filter(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<payout_types::PayoutListFilterConstraints>,
) -> HttpResponse {
    let flow = Flow::PayoutsList;
    let payload = json_payload.into_inner();

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


pub struct DeleteTokenizeDataWorkflow;


impl api::PaymentSession for Worldpay {}

impl api::PaymentSession for for Worldpay


/// Trait for providing a method on custom types for constructing `Maskable`
pub trait Mask {
    /// The type returned by the `into_masked()` method. Must implement `PartialEq`, `Eq` and `Clone`
    type Output: Eq + Clone + PartialEq;

    /// Construct a `Maskable` instance that wraps `Self::Output` by consuming `self`
    fn into_masked(self) -> Maskable<Self::Output>;
}


pub struct PayboxRouterData<T> {
    pub amount: MinorUnit,
    pub router_data: T,
}


pub async fn add_refund_sync_task(
    db: &dyn db::StorageInterface,
    refund: &diesel_refund::Refund,
    runner: storage::ProcessTrackerRunner,
) -> RouterResult<storage::ProcessTracker> {
    let task = "SYNC_REFUND";
    let process_tracker_id = format!("{runner}_{task}_{}", refund.internal_reference_id);
    let schedule_time =
        get_refund_sync_process_schedule_time(db, &refund.connector, &refund.merchant_id, 0)
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to fetch schedule time for refund sync process")?
            .unwrap_or_else(common_utils::date_time::now);
    let refund_workflow_tracking_data = refund_to_refund_core_workflow_model(refund);
    let tag = ["REFUND"];
    let process_tracker_entry = storage::ProcessTrackerNew::new(
        process_tracker_id,
        task,
        runner,
        tag,
        refund_workflow_tracking_data,
        None,
        schedule_time,
        common_types::consts::API_VERSION,
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to construct refund sync process tracker task")?;

    let response = db
        .insert_process(process_tracker_entry)
        .await
        .to_duplicate_response(errors::ApiErrorResponse::DuplicateRefundRequest)
        .attach_printable_lazy(|| {
            format!(
                "Failed while inserting task in process_tracker: refund_id: {}",
                refund.refund_id
            )
        })?;
    metrics::TASKS_ADDED_COUNT.add(1, router_env::metric_attributes!(("flow", "Refund")));

    Ok(response)
}


File: crates/hyperswitch_connectors/src/connectors/inespay/transformers.rs
 Public structs: 12

use common_enums::enums;
use common_utils::{
    request::Method,
    types::{MinorUnit, StringMinorUnit, StringMinorUnitForConnector},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{BankDebitData, PaymentMethodData},
    router_data::{ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{self, PaymentsAuthorizeRequestData, RouterData as _},
};
pub struct InespayRouterData<T> {
    pub amount: StringMinorUnit,
    pub router_data: T,
}

impl<T> From<(StringMinorUnit, T)> for InespayRouterData<T> {
    fn from((amount, item): (StringMinorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Default, Debug, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InespayPaymentsRequest {
    description: String,
    amount: StringMinorUnit,
    reference: String,
    debtor_account: Option<Secret<String>>,
    success_link_redirect: Option<String>,
    notif_url: Option<String>,
}

impl TryFrom<&InespayRouterData<&PaymentsAuthorizeRouterData>> for InespayPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &InespayRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { iban, .. }) => {
                let order_id = item.router_data.connector_request_reference_id.clone();
                let webhook_url = item.router_data.request.get_webhook_url()?;
                let return_url = item.router_data.request.get_router_return_url()?;
                Ok(Self {
                    description: item.router_data.get_description()?,
                    amount: item.amount.clone(),
                    reference: order_id,
                    debtor_account: Some(iban),
                    success_link_redirect: Some(return_url),
                    notif_url: Some(webhook_url),
                })
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

pub struct InespayAuthType {
    pub(super) api_key: Secret<String>,
    pub authorization: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for InespayAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                api_key: api_key.to_owned(),
                authorization: key1.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InespayPaymentsResponseData {
    status: String,
    status_desc: String,
    single_payin_id: String,
    single_payin_link: String,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum InespayPaymentsResponse {
    InespayPaymentsData(InespayPaymentsResponseData),
    InespayPaymentsError(InespayErrorResponse),
}

impl<F, T> TryFrom<ResponseRouterData<F, InespayPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, InespayPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let (status, response) = match item.response {
            InespayPaymentsResponse::InespayPaymentsData(data) => {
                let redirection_url = Url::parse(data.single_payin_link.as_str())
                    .change_context(errors::ConnectorError::ParsingFailed)?;
                let redirection_data = RedirectForm::from((redirection_url, Method::Get));

                (
                    common_enums::AttemptStatus::AuthenticationPending,
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(
                            data.single_payin_id.clone(),
                        ),
                        redirection_data: Box::new(Some(redirection_data)),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: None,
                        incremental_authorization_allowed: None,
                        charges: None,
                    }),
                )
            }
            InespayPaymentsResponse::InespayPaymentsError(data) => (
                common_enums::AttemptStatus::Failure,
                Err(ErrorResponse {
                    code: data.status.clone(),
                    message: data.status_desc.clone(),
                    reason: Some(data.status_desc.clone()),
                    attempt_status: None,
                    connector_transaction_id: None,
                    status_code: item.http_code,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
            ),
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum InespayPSyncStatus {
    Ok,
    Created,
    Opened,
    BankSelected,
    Initiated,
    Pending,
    Aborted,
    Unfinished,
    Rejected,
    Cancelled,
    PartiallyAccepted,
    Failed,
    Settled,
    PartRefunded,
    Refunded,
}

impl From<InespayPSyncStatus> for common_enums::AttemptStatus {
    fn from(item: InespayPSyncStatus) -> Self {
        match item {
            InespayPSyncStatus::Ok | InespayPSyncStatus::Settled => Self::Charged,
            InespayPSyncStatus::Created
            | InespayPSyncStatus::Opened
            | InespayPSyncStatus::BankSelected
            | InespayPSyncStatus::Initiated
            | InespayPSyncStatus::Pending
            | InespayPSyncStatus::Unfinished
            | InespayPSyncStatus::PartiallyAccepted => Self::AuthenticationPending,
            InespayPSyncStatus::Aborted
            | InespayPSyncStatus::Rejected
            | InespayPSyncStatus::Cancelled
            | InespayPSyncStatus::Failed => Self::Failure,
            InespayPSyncStatus::PartRefunded | InespayPSyncStatus::Refunded => Self::AutoRefunded,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InespayPSyncResponseData {
    cod_status: InespayPSyncStatus,
    status_desc: String,
    single_payin_id: String,
    single_payin_link: String,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum InespayPSyncResponse {
    InespayPSyncData(InespayPSyncResponseData),
    InespayPSyncWebhook(InespayPaymentWebhookData),
    InespayPSyncError(InespayErrorResponse),
}

impl<F, T> TryFrom<ResponseRouterData<F, InespayPSyncResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, InespayPSyncResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        match item.response {
            InespayPSyncResponse::InespayPSyncData(data) => {
                let redirection_url = Url::parse(data.single_payin_link.as_str())
                    .change_context(errors::ConnectorError::ParsingFailed)?;
                let redirection_data = RedirectForm::from((redirection_url, Method::Get));

                Ok(Self {
                    status: common_enums::AttemptStatus::from(data.cod_status),
                    response: Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(
                            data.single_payin_id.clone(),
                        ),
                        redirection_data: Box::new(Some(redirection_data)),
                        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
                })
            }
            InespayPSyncResponse::InespayPSyncWebhook(data) => {
                let status = enums::AttemptStatus::from(data.cod_status);
                Ok(Self {
                    status,
                    response: Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(
                            data.single_payin_id.clone(),
                        ),
                        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
                })
            }
            InespayPSyncResponse::InespayPSyncError(data) => Ok(Self {
                response: Err(ErrorResponse {
                    code: data.status.clone(),
                    message: data.status_desc.clone(),
                    reason: Some(data.status_desc.clone()),
                    attempt_status: None,
                    connector_transaction_id: None,
                    status_code: item.http_code,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
                ..item.data
            }),
        }
    }
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InespayRefundRequest {
    single_payin_id: String,
    amount: Option<MinorUnit>,
}

impl<F> TryFrom<&InespayRouterData<&RefundsRouterData<F>>> for InespayRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &InespayRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let amount = utils::convert_back_amount_to_minor_units(
            &StringMinorUnitForConnector,
            item.amount.to_owned(),
            item.router_data.request.currency,
        )?;
        Ok(Self {
            single_payin_id: item.router_data.request.connector_transaction_id.clone(),
            amount: Some(amount),
        })
    }
}

#[derive(Debug, Serialize, Default, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum InespayRSyncStatus {
    Confirmed,
    #[default]
    Pending,
    Rejected,
    Denied,
    Reversed,
    Mistake,
}

impl From<InespayRSyncStatus> for enums::RefundStatus {
    fn from(item: InespayRSyncStatus) -> Self {
        match item {
            InespayRSyncStatus::Confirmed => Self::Success,
            InespayRSyncStatus::Pending => Self::Pending,
            InespayRSyncStatus::Rejected
            | InespayRSyncStatus::Denied
            | InespayRSyncStatus::Reversed
            | InespayRSyncStatus::Mistake => Self::Failure,
        }
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RefundsData {
    status: String,
    status_desc: String,
    refund_id: String,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum InespayRefundsResponse {
    InespayRefundsData(RefundsData),
    InespayRefundsError(InespayErrorResponse),
}

impl TryFrom<RefundsResponseRouterData<Execute, InespayRefundsResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, InespayRefundsResponse>,
    ) -> Result<Self, Self::Error> {
        match item.response {
            InespayRefundsResponse::InespayRefundsData(data) => Ok(Self {
                response: Ok(RefundsResponseData {
                    connector_refund_id: data.refund_id,
                    refund_status: enums::RefundStatus::Pending,
                }),
                ..item.data
            }),
            InespayRefundsResponse::InespayRefundsError(data) => Ok(Self {
                response: Err(ErrorResponse {
                    code: data.status.clone(),
                    message: data.status_desc.clone(),
                    reason: Some(data.status_desc.clone()),
                    attempt_status: None,
                    connector_transaction_id: None,
                    status_code: item.http_code,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
                ..item.data
            }),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InespayRSyncResponseData {
    cod_status: InespayRSyncStatus,
    status_desc: String,
    refund_id: String,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum InespayRSyncResponse {
    InespayRSyncData(InespayRSyncResponseData),
    InespayRSyncWebhook(InespayRefundWebhookData),
    InespayRSyncError(InespayErrorResponse),
}

impl TryFrom<RefundsResponseRouterData<RSync, InespayRSyncResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, InespayRSyncResponse>,
    ) -> Result<Self, Self::Error> {
        let response = match item.response {
            InespayRSyncResponse::InespayRSyncData(data) => Ok(RefundsResponseData {
                connector_refund_id: data.refund_id,
                refund_status: enums::RefundStatus::from(data.cod_status),
            }),
            InespayRSyncResponse::InespayRSyncWebhook(data) => Ok(RefundsResponseData {
                connector_refund_id: data.refund_id,
                refund_status: enums::RefundStatus::from(data.cod_status),
            }),
            InespayRSyncResponse::InespayRSyncError(data) => Err(ErrorResponse {
                code: data.status.clone(),
                message: data.status_desc.clone(),
                reason: Some(data.status_desc.clone()),
                attempt_status: None,
                connector_transaction_id: None,
                status_code: item.http_code,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
        };
        Ok(Self {
            response,
            ..item.data
        })
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct InespayPaymentWebhookData {
    pub single_payin_id: String,
    pub cod_status: InespayPSyncStatus,
    pub description: String,
    pub amount: MinorUnit,
    pub reference: String,
    pub creditor_account: Secret<String>,
    pub debtor_name: Secret<String>,
    pub debtor_account: Secret<String>,
    pub custom_data: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct InespayRefundWebhookData {
    pub refund_id: String,
    pub simple_payin_id: String,
    pub cod_status: InespayRSyncStatus,
    pub description: String,
    pub amount: MinorUnit,
    pub reference: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum InespayWebhookEventData {
    Payment(InespayPaymentWebhookData),
    Refund(InespayRefundWebhookData),
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct InespayWebhookEvent {
    pub data_return: String,
    pub signature_data_return: String,
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InespayErrorResponse {
    pub status: String,
    pub status_desc: String,
}

impl From<InespayWebhookEventData> for api_models::webhooks::IncomingWebhookEvent {
    fn from(item: InespayWebhookEventData) -> Self {
        match item {
            InespayWebhookEventData::Payment(payment_data) => match payment_data.cod_status {
                InespayPSyncStatus::Ok | InespayPSyncStatus::Settled => Self::PaymentIntentSuccess,
                InespayPSyncStatus::Failed | InespayPSyncStatus::Rejected => {
                    Self::PaymentIntentFailure
                }
                InespayPSyncStatus::Created
                | InespayPSyncStatus::Opened
                | InespayPSyncStatus::BankSelected
                | InespayPSyncStatus::Initiated
                | InespayPSyncStatus::Pending
                | InespayPSyncStatus::Unfinished
                | InespayPSyncStatus::PartiallyAccepted => Self::PaymentIntentProcessing,
                InespayPSyncStatus::Aborted
                | InespayPSyncStatus::Cancelled
                | InespayPSyncStatus::PartRefunded
                | InespayPSyncStatus::Refunded => Self::EventNotSupported,
            },
            InespayWebhookEventData::Refund(refund_data) => match refund_data.cod_status {
                InespayRSyncStatus::Confirmed => Self::RefundSuccess,
                InespayRSyncStatus::Rejected
                | InespayRSyncStatus::Denied
                | InespayRSyncStatus::Reversed
                | InespayRSyncStatus::Mistake => Self::RefundFailure,
                InespayRSyncStatus::Pending => Self::EventNotSupported,
            },
        }
    }
}


impl ConnectorAccessToken for Threedsecureio {}

impl ConnectorAccessToken for for Threedsecureio


impl Tokenizable for PaymentsAuthorizeData {
    fn set_session_token(&mut self, token: Option<String>) {
        self.session_token = token;
    }
}

impl Tokenizable for for PaymentsAuthorizeData


pub struct FiservResponseInteractions {
    actions: FiservResponseActions,
}


pub struct AmountDetails {
    /// The amount of the order in the lowest denomination of currency
    pub order_amount: MinorUnit,
    /// The currency of the order
    pub currency: common_enums::Currency,
    /// The shipping cost of the order. This has to be collected from the merchant
    pub shipping_cost: Option<MinorUnit>,
    /// Tax details related to the order. This will be calculated by the external tax provider
    pub tax_details: Option<TaxDetails>,
    /// The action to whether calculate tax by calling external tax provider or not
    pub skip_external_tax_calculation: common_enums::TaxCalculationOverride,
    /// The action to whether calculate surcharge or not
    pub skip_surcharge_calculation: common_enums::SurchargeCalculationOverride,
    /// The surcharge amount to be added to the order, collected from the merchant
    pub surcharge_amount: Option<MinorUnit>,
    /// tax on surcharge amount
    pub tax_on_surcharge: Option<MinorUnit>,
    /// The total amount captured for the order. This is the sum of all the captured amounts for the order.
    /// For automatic captures, this will be the same as net amount for the order
    pub amount_captured: Option<MinorUnit>,
}


pub struct GocardlessCustomerRequest {
    customers: GocardlessCustomer,
}


/// Constructs a new AwsSes client
    pub async fn create(
        conf: &EmailSettings,
        ses_config: &SESConfig,
        proxy_url: Option<impl AsRef<str>>,
    ) -> Self {
        // Build the client initially which will help us know if the email configuration is correct
        Self::create_client(conf, ses_config, proxy_url)
            .await
            .map_err(|error| logger::error!(?error, "Failed to initialize SES Client"))
            .ok();

        Self {
            sender: conf.sender_email.clone(),
            ses_config: ses_config.clone(),
            settings: conf.clone(),
        }
    }