File: crates/hyperswitch_connectors/src/connectors/stripebilling/transformers.rs
 Public functions: 2
 Public structs: 22

#[cfg(feature = "v2")]
use std::str::FromStr;

use common_enums::enums;
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
use common_utils::id_type;
use common_utils::{errors::CustomResult, ext_traits::ByteSliceExt, types::StringMinorUnit};
use error_stack::ResultExt;
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
use hyperswitch_domain_models::revenue_recovery;
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},
};
#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
use hyperswitch_domain_models::{
    router_flow_types::revenue_recovery as recovery_router_flows,
    router_request_types::revenue_recovery as recovery_request_types,
    router_response_types::revenue_recovery as recovery_response_types,
    types as recovery_router_data_types,
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{convert_uppercase, PaymentsAuthorizeRequestData},
};
pub mod auth_headers {
    pub const STRIPE_API_VERSION: &str = "stripe-version";
    pub const STRIPE_VERSION: &str = "2022-11-15";
}

//TODO: Fill the struct with respective fields
pub struct StripebillingRouterData<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 StripebillingRouterData<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 StripebillingPaymentsRequest {
    amount: StringMinorUnit,
    card: StripebillingCard,
}

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

impl TryFrom<&StripebillingRouterData<&PaymentsAuthorizeRouterData>>
    for StripebillingPaymentsRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &StripebillingRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = StripebillingCard {
                    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 StripebillingAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for StripebillingAuthType {
    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, Copy)]
#[serde(rename_all = "lowercase")]
pub enum StripebillingPaymentStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

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

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

impl<F, T> TryFrom<ResponseRouterData<F, StripebillingPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, StripebillingPaymentsResponse, 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 StripebillingRefundRequest {
    pub amount: StringMinorUnit,
}

impl<F> TryFrom<&StripebillingRouterData<&RefundsRouterData<F>>> for StripebillingRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &StripebillingRouterData<&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, Copy)]
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 StripebillingErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct StripebillingWebhookBody {
    #[serde(rename = "type")]
    pub event_type: StripebillingEventType,
    pub data: StripebillingWebhookData,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct StripebillingInvoiceBody {
    #[serde(rename = "type")]
    pub event_type: StripebillingEventType,
    pub data: StripebillingInvoiceData,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum StripebillingEventType {
    #[serde(rename = "invoice.paid")]
    PaymentSucceeded,
    #[serde(rename = "invoice.payment_failed")]
    PaymentFailed,
    #[serde(rename = "invoice.voided")]
    InvoiceDeleted,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct StripebillingWebhookData {
    pub object: StripebillingWebhookObject,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct StripebillingInvoiceData {
    pub object: StripebillingWebhookObject,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct StripebillingWebhookObject {
    #[serde(rename = "id")]
    pub invoice_id: String,
    #[serde(deserialize_with = "convert_uppercase")]
    pub currency: enums::Currency,
    pub customer: String,
    #[serde(rename = "amount_remaining")]
    pub amount: common_utils::types::MinorUnit,
    pub charge: String,
    pub payment_intent: String,
    pub customer_address: Option<StripebillingInvoiceBillingAddress>,
    pub attempt_count: u16,
    pub lines: StripebillingWebhookLinesObject,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct StripebillingWebhookLinesObject {
    pub data: Vec<StripebillingWebhookLinesData>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct StripebillingWebhookLinesData {
    pub period: StripebillingWebhookLineDataPeriod,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct StripebillingWebhookLineDataPeriod {
    #[serde(with = "common_utils::custom_serde::timestamp")]
    pub end: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::timestamp")]
    pub start: PrimitiveDateTime,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct StripebillingInvoiceBillingAddress {
    pub country: Option<enums::CountryAlpha2>,
    pub city: Option<String>,
    pub address_line1: Option<Secret<String>>,
    pub address_line2: Option<Secret<String>>,
    pub zip_code: Option<Secret<String>>,
    pub state: Option<Secret<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct StripebillingInvoiceObject {
    #[serde(rename = "id")]
    pub invoice_id: String,
    #[serde(deserialize_with = "convert_uppercase")]
    pub currency: enums::Currency,
    #[serde(rename = "amount_remaining")]
    pub amount: common_utils::types::MinorUnit,
    pub attempt_count: Option<u16>,
}

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

        Ok(webhook_body)
    }
}

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

impl From<StripebillingInvoiceBillingAddress> for api_models::payments::Address {
    fn from(item: StripebillingInvoiceBillingAddress) -> Self {
        Self {
            address: Some(api_models::payments::AddressDetails::from(item)),
            phone: None,
            email: None,
        }
    }
}

impl From<StripebillingInvoiceBillingAddress> for api_models::payments::AddressDetails {
    fn from(item: StripebillingInvoiceBillingAddress) -> Self {
        Self {
            city: item.city,
            state: item.state,
            country: item.country,
            zip: item.zip_code,
            line1: item.address_line1,
            line2: item.address_line2,
            line3: None,
            first_name: None,
            last_name: None,
            origin_zip: None,
        }
    }
}

#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
impl TryFrom<StripebillingInvoiceBody> for revenue_recovery::RevenueRecoveryInvoiceData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: StripebillingInvoiceBody) -> Result<Self, Self::Error> {
        let merchant_reference_id =
            id_type::PaymentReferenceId::from_str(&item.data.object.invoice_id)
                .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let next_billing_at = item
            .data
            .object
            .lines
            .data
            .first()
            .map(|linedata| linedata.period.end);
        let billing_started_at = item
            .data
            .object
            .lines
            .data
            .first()
            .map(|linedata| linedata.period.start);
        Ok(Self {
            amount: item.data.object.amount,
            currency: item.data.object.currency,
            merchant_reference_id,
            billing_address: item
                .data
                .object
                .customer_address
                .map(api_models::payments::Address::from),
            retry_count: Some(item.data.object.attempt_count),
            next_billing_at,
            billing_started_at,
        })
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct StripebillingRecoveryDetailsData {
    #[serde(rename = "id")]
    pub charge_id: String,
    pub status: StripebillingChargeStatus,
    pub amount: common_utils::types::MinorUnit,
    #[serde(deserialize_with = "convert_uppercase")]
    pub currency: enums::Currency,
    pub customer: String,
    pub payment_method: String,
    pub failure_code: Option<String>,
    pub failure_message: Option<String>,
    #[serde(with = "common_utils::custom_serde::timestamp")]
    pub created: PrimitiveDateTime,
    pub payment_method_details: StripePaymentMethodDetails,
    #[serde(rename = "invoice")]
    pub invoice_id: String,
    pub payment_intent: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct StripePaymentMethodDetails {
    #[serde(rename = "type")]
    pub type_of_payment_method: StripebillingPaymentMethod,
    #[serde(rename = "card")]
    pub card_details: StripeBillingCardDetails,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum StripebillingPaymentMethod {
    Card,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct StripeBillingCardDetails {
    pub network: StripebillingCardNetwork,
    pub funding: StripebillingFundingTypes,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum StripebillingCardNetwork {
    Visa,
    Mastercard,
    AmericanExpress,
    JCB,
    DinersClub,
    Discover,
    CartesBancaires,
    UnionPay,
    Interac,
    RuPay,
    Maestro,
    Star,
    Pulse,
    Accel,
    Nyce,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[serde(rename = "snake_case")]
pub enum StripebillingFundingTypes {
    #[serde(rename = "credit")]
    Credit,
    #[serde(rename = "debit")]
    Debit,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum StripebillingChargeStatus {
    Succeeded,
    Failed,
}

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
// This is the default hard coded mca Id to find the stripe account associated with the stripe biliing
// Context : Since we dont have the concept of connector_reference_id in stripebilling because payments always go through stripe.
// While creating stripebilling we will hard code the stripe account id to string "stripebilling" in mca featrue metadata. So we have to pass the same as account_reference_id here in response.
const MCA_ID_IDENTIFIER_FOR_STRIPE_IN_STRIPEBILLING_MCA_FEAATURE_METADATA: &str = "stripebilling";

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
impl
    TryFrom<
        ResponseRouterData<
            recovery_router_flows::BillingConnectorPaymentsSync,
            StripebillingRecoveryDetailsData,
            recovery_request_types::BillingConnectorPaymentsSyncRequest,
            recovery_response_types::BillingConnectorPaymentsSyncResponse,
        >,
    > for recovery_router_data_types::BillingConnectorPaymentsSyncRouterData
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            recovery_router_flows::BillingConnectorPaymentsSync,
            StripebillingRecoveryDetailsData,
            recovery_request_types::BillingConnectorPaymentsSyncRequest,
            recovery_response_types::BillingConnectorPaymentsSyncResponse,
        >,
    ) -> Result<Self, Self::Error> {
        let charge_details = item.response;
        let merchant_reference_id =
            id_type::PaymentReferenceId::from_str(charge_details.invoice_id.as_str())
                .change_context(errors::ConnectorError::MissingRequiredField {
                    field_name: "invoice_id",
                })?;
        let connector_transaction_id = Some(common_utils::types::ConnectorTransactionId::from(
            charge_details.payment_intent,
        ));

        Ok(Self {
            response: Ok(
                recovery_response_types::BillingConnectorPaymentsSyncResponse {
                    status: charge_details.status.into(),
                    amount: charge_details.amount,
                    currency: charge_details.currency,
                    merchant_reference_id,
                    connector_account_reference_id:
                        MCA_ID_IDENTIFIER_FOR_STRIPE_IN_STRIPEBILLING_MCA_FEAATURE_METADATA
                            .to_string(),
                    connector_transaction_id,
                    error_code: charge_details.failure_code,
                    error_message: charge_details.failure_message,
                    processor_payment_method_token: charge_details.payment_method,
                    connector_customer_id: charge_details.customer,
                    transaction_created_at: Some(charge_details.created),
                    payment_method_sub_type: common_enums::PaymentMethodType::from(
                        charge_details.payment_method_details.card_details.funding,
                    ),
                    payment_method_type: common_enums::PaymentMethod::from(
                        charge_details.payment_method_details.type_of_payment_method,
                    ),
                    // Todo: Fetch Card issuer details. Generally in the other billing connector we are getting card_issuer using the card bin info. But stripe dosent provide any such details. We should find a way for stripe billing case
                    charge_id: Some(charge_details.charge_id.clone()),
                    // Need to populate these card info field
                    card_info: api_models::payments::AdditionalCardInfo {
                        card_network: Some(common_enums::CardNetwork::from(
                            charge_details.payment_method_details.card_details.network,
                        )),
                        card_isin: None,
                        card_issuer: None,
                        card_type: None,
                        card_issuing_country: None,
                        bank_code: None,
                        last4: None,
                        card_extended_bin: None,
                        card_exp_month: None,
                        card_exp_year: None,
                        card_holder_name: None,
                        payment_checks: None,
                        authentication_data: None,
                        is_regulated: None,
                        signature_network: None,
                    },
                },
            ),
            ..item.data
        })
    }
}

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
impl From<StripebillingChargeStatus> for enums::AttemptStatus {
    fn from(status: StripebillingChargeStatus) -> Self {
        match status {
            StripebillingChargeStatus::Succeeded => Self::Charged,
            StripebillingChargeStatus::Failed => Self::Failure,
        }
    }
}

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
impl From<StripebillingFundingTypes> for common_enums::PaymentMethodType {
    fn from(funding: StripebillingFundingTypes) -> Self {
        match funding {
            StripebillingFundingTypes::Credit => Self::Credit,
            StripebillingFundingTypes::Debit => Self::Debit,
        }
    }
}

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
impl From<StripebillingPaymentMethod> for common_enums::PaymentMethod {
    fn from(method: StripebillingPaymentMethod) -> Self {
        match method {
            StripebillingPaymentMethod::Card => Self::Card,
        }
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct StripebillingRecordBackResponse {
    pub id: String,
}

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
impl
    TryFrom<
        ResponseRouterData<
            recovery_router_flows::RecoveryRecordBack,
            StripebillingRecordBackResponse,
            recovery_request_types::RevenueRecoveryRecordBackRequest,
            recovery_response_types::RevenueRecoveryRecordBackResponse,
        >,
    > for recovery_router_data_types::RevenueRecoveryRecordBackRouterData
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            recovery_router_flows::RecoveryRecordBack,
            StripebillingRecordBackResponse,
            recovery_request_types::RevenueRecoveryRecordBackRequest,
            recovery_response_types::RevenueRecoveryRecordBackResponse,
        >,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(recovery_response_types::RevenueRecoveryRecordBackResponse {
                merchant_reference_id: id_type::PaymentReferenceId::from_str(
                    item.response.id.as_str(),
                )
                .change_context(errors::ConnectorError::MissingRequiredField {
                    field_name: "invoice_id in the response",
                })?,
            }),
            ..item.data
        })
    }
}

impl From<StripebillingCardNetwork> for enums::CardNetwork {
    fn from(item: StripebillingCardNetwork) -> Self {
        match item {
            StripebillingCardNetwork::Visa => Self::Visa,
            StripebillingCardNetwork::Mastercard => Self::Mastercard,
            StripebillingCardNetwork::AmericanExpress => Self::AmericanExpress,
            StripebillingCardNetwork::JCB => Self::JCB,
            StripebillingCardNetwork::DinersClub => Self::DinersClub,
            StripebillingCardNetwork::Discover => Self::Discover,
            StripebillingCardNetwork::CartesBancaires => Self::CartesBancaires,
            StripebillingCardNetwork::UnionPay => Self::UnionPay,
            StripebillingCardNetwork::Interac => Self::Interac,
            StripebillingCardNetwork::RuPay => Self::RuPay,
            StripebillingCardNetwork::Maestro => Self::Maestro,
            StripebillingCardNetwork::Star => Self::Star,
            StripebillingCardNetwork::Pulse => Self::Pulse,
            StripebillingCardNetwork::Accel => Self::Accel,
            StripebillingCardNetwork::Nyce => Self::Nyce,
        }
    }
}


Documentation: api-reference/v2/merchant-connector-account/merchant-connector--delete.mdx
# Type: Doc File

---
openapi: delete /v2/connector-accounts/{id}
---


impl api::PaymentAuthorize for Opayo {}

impl api::PaymentAuthorize for for Opayo


impl RefundResponse {
    pub fn get_refund_id_as_string(&self) -> String {
        self.id.get_string_repr().to_owned()
    }
}


impl IntoContext for BackendInput {
    fn into_context(self) -> Result<Vec<dir::DirValue>, KgraphError> {
        let mut ctx: Vec<dir::DirValue> = Vec::new();

        ctx.push(dir::DirValue::PaymentAmount(NumValue {
            number: self.payment.amount,
            refinement: None,
        }));

        ctx.push(dir::DirValue::PaymentCurrency(self.payment.currency));

        if let Some(auth_type) = self.payment.authentication_type {
            ctx.push(dir::DirValue::AuthenticationType(auth_type));
        }

        if let Some(capture_method) = self.payment.capture_method {
            ctx.push(dir::DirValue::CaptureMethod(capture_method));
        }

        if let Some(business_country) = self.payment.business_country {
            ctx.push(dir::DirValue::BusinessCountry(business_country));
        }
        if let Some(business_label) = self.payment.business_label {
            ctx.push(dir::DirValue::BusinessLabel(StrValue {
                value: business_label,
            }));
        }
        if let Some(billing_country) = self.payment.billing_country {
            ctx.push(dir::DirValue::BillingCountry(billing_country));
        }

        if let Some(payment_method) = self.payment_method.payment_method {
            ctx.push(dir::DirValue::PaymentMethod(payment_method));
        }

        if let (Some(pm_type), Some(payment_method)) = (
            self.payment_method.payment_method_type,
            self.payment_method.payment_method,
        ) {
            ctx.push((pm_type, payment_method).into_dir_value()?)
        }

        if let Some(card_network) = self.payment_method.card_network {
            ctx.push(dir::DirValue::CardNetwork(card_network));
        }
        if let Some(setup_future_usage) = self.payment.setup_future_usage {
            ctx.push(dir::DirValue::SetupFutureUsage(setup_future_usage));
        }
        if let Some(mandate_acceptance_type) = self.mandate.mandate_acceptance_type {
            ctx.push(dir::DirValue::MandateAcceptanceType(
                mandate_acceptance_type,
            ));
        }
        if let Some(mandate_type) = self.mandate.mandate_type {
            ctx.push(dir::DirValue::MandateType(mandate_type));
        }
        if let Some(payment_type) = self.mandate.payment_type {
            ctx.push(dir::DirValue::PaymentType(payment_type));
        }

        Ok(ctx)
    }
}


pub async fn get_sso_id_from_redis(
    state: &SessionState,
    oidc_state: Secret<String>,
) -> UserResult<String> {
    let connection = get_redis_connection_for_global_tenant(state)?;
    let key = get_oidc_key(&oidc_state.expose());
    connection
        .get_key::<Option<String>>(&key.into())
        .await
        .change_context(UserErrors::InternalServerError)
        .attach_printable("Failed to get sso id from redis")?
        .ok_or(UserErrors::SSOFailed)
        .attach_printable("Cannot find oidc state in redis. Oidc state invalid or expired")
}


pub struct WellsfargoPaymentsCaptureRequest {
    processing_information: ProcessingInformation,
    order_information: OrderInformationWithBill,
    client_reference_information: ClientReferenceInformation,
    #[serde(skip_serializing_if = "Option::is_none")]
    merchant_defined_information: Option<Vec<MerchantDefinedInformation>>,
}


impl State for PaymentMethodCreated {}


pub struct DeviceData;


pub struct BlikData {
    blik: BlikDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}


impl IncomingWebhook for Paypal {
    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let payload: paypal::PaypalWebhooksBody =
            request
                .body
                .parse_struct("PaypalWebhooksBody")
                .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        match payload.resource {
            paypal::PaypalResource::PaypalCardWebhooks(resource) => {
                Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                    api_models::payments::PaymentIdType::ConnectorTransactionId(
                        resource.supplementary_data.related_ids.order_id,
                    ),
                ))
            }
            paypal::PaypalResource::PaypalRedirectsWebhooks(resource) => {
                Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                    api_models::payments::PaymentIdType::PaymentAttemptId(
                        resource
                            .purchase_units
                            .first()
                            .and_then(|unit| unit.invoice_id.clone().or(unit.reference_id.clone()))
                            .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                    ),
                ))
            }
            paypal::PaypalResource::PaypalRefundWebhooks(resource) => {
                Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                    api_models::webhooks::RefundIdType::ConnectorRefundId(resource.id),
                ))
            }
            paypal::PaypalResource::PaypalDisputeWebhooks(resource) => {
                Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                    api_models::payments::PaymentIdType::ConnectorTransactionId(
                        resource
                            .disputed_transactions
                            .first()
                            .map(|transaction| transaction.seller_transaction_id.clone())
                            .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                    ),
                ))
            }
            #[cfg(feature = "payouts")]
            paypal::PaypalResource::PaypalBatchPayoutWebhooks(resource) => {
                Ok(api_models::webhooks::ObjectReferenceId::PayoutId(
                    api_models::webhooks::PayoutIdType::ConnectorPayoutId(
                        resource.batch_header.payout_batch_id,
                    ),
                ))
            }
            #[cfg(feature = "payouts")]
            paypal::PaypalResource::PaypalItemPayoutWebhooks(resource) => {
                Ok(api_models::webhooks::ObjectReferenceId::PayoutId(
                    api_models::webhooks::PayoutIdType::PayoutAttemptId(resource.sender_batch_id),
                ))
            }
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let payload: paypal::PaypalWebooksEventType = request
            .body
            .parse_struct("PaypalWebooksEventType")
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
        let outcome = match payload.event_type {
            PaypalWebhookEventType::CustomerDisputeResolved => Some(
                request
                    .body
                    .parse_struct::<paypal::DisputeOutcome>("PaypalWebooksEventType")
                    .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?
                    .outcome_code,
            ),
            PaypalWebhookEventType::CustomerDisputeCreated
            | PaypalWebhookEventType::RiskDisputeCreated
            | PaypalWebhookEventType::CustomerDisputedUpdated
            | PaypalWebhookEventType::PaymentAuthorizationCreated
            | PaypalWebhookEventType::PaymentAuthorizationVoided
            | PaypalWebhookEventType::PaymentCaptureDeclined
            | PaypalWebhookEventType::PaymentCaptureCompleted
            | PaypalWebhookEventType::PaymentCapturePending
            | PaypalWebhookEventType::PaymentCaptureRefunded
            | PaypalWebhookEventType::CheckoutOrderApproved
            | PaypalWebhookEventType::CheckoutOrderCompleted
            | PaypalWebhookEventType::CheckoutOrderProcessed
            | PaypalWebhookEventType::Unknown => None,
            #[cfg(feature = "payouts")]
            PaypalWebhookEventType::PayoutsBatchDenied
            | PaypalWebhookEventType::PayoutsBatchProcessing
            | PaypalWebhookEventType::PayoutsBatchSuccess
            | PaypalWebhookEventType::PayoutsItemBlocked
            | PaypalWebhookEventType::PayoutsItemCanceled
            | PaypalWebhookEventType::PayoutsItemDenied
            | PaypalWebhookEventType::PayoutsItemFailed
            | PaypalWebhookEventType::PayoutsItemHeld
            | PaypalWebhookEventType::PayoutsItemRefunded
            | PaypalWebhookEventType::PayoutsItemReturned
            | PaypalWebhookEventType::PayoutsItemSuccess
            | PaypalWebhookEventType::PayoutsItemUnclaimed => None,
        };

        Ok(transformers::get_payapl_webhooks_event(
            payload.event_type,
            outcome,
        ))
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let details: paypal::PaypalWebhooksBody =
            request
                .body
                .parse_struct("PaypalWebhooksBody")
                .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
        Ok(match details.resource {
            paypal::PaypalResource::PaypalCardWebhooks(resource) => Box::new(
                paypal::PaypalPaymentsSyncResponse::try_from((*resource, details.event_type))?,
            ),
            paypal::PaypalResource::PaypalRedirectsWebhooks(resource) => Box::new(
                paypal::PaypalOrdersResponse::try_from((*resource, details.event_type))?,
            ),
            paypal::PaypalResource::PaypalRefundWebhooks(resource) => Box::new(
                paypal::RefundSyncResponse::try_from((*resource, details.event_type))?,
            ),
            paypal::PaypalResource::PaypalDisputeWebhooks(_) => Box::new(details),
            #[cfg(feature = "payouts")]
            paypal::PaypalResource::PaypalBatchPayoutWebhooks(resource) => {
                Box::new(paypal::PaypalPayoutSyncResponse::try_from(*resource)?)
            }
            #[cfg(feature = "payouts")]
            paypal::PaypalResource::PaypalItemPayoutWebhooks(resource) => Box::new(
                paypal::PaypalPayoutSyncResponse::try_from((*resource, details.event_type))?,
            ),
        })
    }

    fn get_dispute_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<disputes::DisputePayload, errors::ConnectorError> {
        let webhook_payload: paypal::PaypalWebhooksBody = request
            .body
            .parse_struct("PaypalWebhooksBody")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        match webhook_payload.resource {
            transformers::PaypalResource::PaypalCardWebhooks(_)
            | transformers::PaypalResource::PaypalRedirectsWebhooks(_)
            | transformers::PaypalResource::PaypalRefundWebhooks(_) => {
                Err(errors::ConnectorError::ResponseDeserializationFailed)
                    .attach_printable("Expected Dispute webhooks,but found other webhooks")?
            }
            #[cfg(feature = "payouts")]
            transformers::PaypalResource::PaypalBatchPayoutWebhooks(_)
            | transformers::PaypalResource::PaypalItemPayoutWebhooks(_) => {
                Err(errors::ConnectorError::ResponseDeserializationFailed)
                    .attach_printable("Expected Dispute webhooks,but found other webhooks")?
            }
            transformers::PaypalResource::PaypalDisputeWebhooks(payload) => {
                let amt = connector_utils::convert_back_amount_to_minor_units(
                    self.amount_converter,
                    payload.dispute_amount.value,
                    payload.dispute_amount.currency_code,
                )?;
                Ok(disputes::DisputePayload {
                    amount: connector_utils::convert_amount(
                        self.amount_converter_webhooks,
                        amt,
                        payload.dispute_amount.currency_code,
                    )?,
                    currency: payload.dispute_amount.currency_code,
                    dispute_stage: api_models::enums::DisputeStage::from(
                        payload.dispute_life_cycle_stage.clone(),
                    ),
                    connector_status: payload.status.to_string(),
                    connector_dispute_id: payload.dispute_id,
                    connector_reason: payload.reason.clone(),
                    connector_reason_code: payload.reason,
                    challenge_required_by: None,
                    created_at: payload.create_time,
                    updated_at: payload.update_time,
                })
            }
        }
    }
}


pub struct BraintreeApiErrorResponse {
    pub api_error_response: ApiErrorResponse,
}


pub struct HipayMaintenanceResponse<S> {
    status: S,
    message: String,
    transaction_reference: String,
}


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

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

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

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

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

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

        let reason = response
            .error
            .violacoes
            .map(|violacoes| {
                violacoes.iter().try_fold(String::new(), |mut acc, error| {
                    write!(
                        acc,
                        " razao - {}, propriedade - {}, valor - {};",
                        error.razao, error.propriedade, error.valor
                    )
                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)
                    .attach_printable("Failed to concatenate error details")
                    .map(|_| acc)
                })
            })
            .transpose()?;

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error.title.unwrap_or(NO_ERROR_CODE.to_string()),
            message: response
                .error
                .detail
                .unwrap_or(NO_ERROR_MESSAGE.to_string()),
            reason,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


pub struct RsyncApplicationInformation {
    status: Option<CybersourceRefundStatus>,
}


pub async fn find_by_card_id(conn: &PgPooledConn, card_id: &str) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::card_id.eq(card_id.to_owned()),
        )
        .await
    }


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

use hyperswitch_domain_models::payment_method_data::{Card, PaymentMethodData};
use masking::Secret;
use router::types::{self, api, storage::enums};
use test_utils::connector_auth;

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

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

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

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

static CONNECTOR: FlexitiTest = FlexitiTest {};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Connector dependent test cases goes here

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


pub struct TokenizeWithCard;


Documentation: api-reference/v1/business-profile/business-profile--update.mdx
# Type: Doc File

---
openapi: post /account/{account_id}/business_profile/{profile_id}
---


pub struct GetnetWebhookNotificationResponseBody {
    pub payment: WebhookResponseData,
}


pub async fn reset_process_sync_task(
    db: &dyn StorageInterface,
    payment_attempt: &storage::PaymentAttempt,
    schedule_time: time::PrimitiveDateTime,
) -> Result<(), errors::ProcessTrackerError> {
    let runner = storage::ProcessTrackerRunner::PaymentsSyncWorkflow;
    let task = "PAYMENTS_SYNC";
    let process_tracker_id = pt_utils::get_process_tracker_id(
        runner,
        task,
        payment_attempt.get_id(),
        &payment_attempt.merchant_id,
    );
    let psync_process = db
        .find_process_by_id(&process_tracker_id)
        .await?
        .ok_or(errors::ProcessTrackerError::ProcessFetchingFailed)?;
    db.as_scheduler()
        .reset_process(psync_process, schedule_time)
        .await?;
    Ok(())
}


pub struct ForteCaptureResponse {
    pub transaction_id: String,
    pub original_transaction_id: String,
    pub entered_by: String,
    pub authorization_code: String,
    pub response: CaptureResponseStatus,
}


pub struct VgsMetadata {
    /// External vault url
    pub proxy_url: Url,
    /// CA certificates to verify the vault server
    pub certificate: Secret<String>,
}


pub trait AsyncLift<U> {
    type SelfWrapper<T>;
    type OtherWrapper<T, E>;

    async fn async_lift<Func, F, E, V>(self, func: Func) -> Self::OtherWrapper<V, E>
    where
        Func: Fn(Self::SelfWrapper<U>) -> F + Send + Sync,
        F: futures::Future<Output = Self::OtherWrapper<V, E>> + Send;
}


pub struct PaymentEvent {
    payment_intent: storage::PaymentIntent,
    payment_attempt: storage::PaymentAttempt,
}


impl api::PaymentAuthorize for Adyen {}

impl api::PaymentAuthorize for for Adyen


pub fn get_allowed_signals() -> Result<signal_hook_tokio::SignalsInfo, std::io::Error> {
    signal_hook_tokio::Signals::new([signal_hook::consts::SIGTERM, signal_hook::consts::SIGINT])
}


pub struct LocalBankTransferAdditionalData {
    /// Partially masked bank code
    #[schema(value_type = Option<String>, example = "**** OA2312")]
    pub bank_code: Option<MaskedBankAccount>,
}


pub struct ClientTokenInput {
    merchant_account_id: Secret<String>,
}


impl api::PaymentSync for Paypal {}


pub async fn generate_vault_session_details(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    merchant_connector_account_type: &domain::MerchantConnectorAccountTypeDetails,
    connector_customer_id: Option<String>,
) -> RouterResult<Option<api::VaultSessionDetails>> {
    let connector_name = merchant_connector_account_type
        .get_connector_name()
        .map(|name| name.to_string())
        .ok_or(errors::ApiErrorResponse::InternalServerError)?; // should not panic since we should always have a connector name
    let connector = api_enums::VaultConnectors::from_str(&connector_name)
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    let connector_auth_type: router_types::ConnectorAuthType = merchant_connector_account_type
        .get_connector_account_details()
        .map_err(|err| {
            err.change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to parse connector auth type")
        })?;

    match (connector, connector_auth_type) {
        // create session for vgs vault
        (
            api_enums::VaultConnectors::Vgs,
            router_types::ConnectorAuthType::SignatureKey { api_secret, .. },
        ) => {
            let sdk_env = match state.conf.env {
                Env::Sandbox | Env::Development => "sandbox",
                Env::Production => "live",
            }
            .to_string();
            Ok(Some(api::VaultSessionDetails::Vgs(
                api::VgsSessionDetails {
                    external_vault_id: api_secret,
                    sdk_env,
                },
            )))
        }
        // create session for hyperswitch vault
        (
            api_enums::VaultConnectors::HyperswitchVault,
            router_types::ConnectorAuthType::SignatureKey {
                key1, api_secret, ..
            },
        ) => {
            generate_hyperswitch_vault_session_details(
                state,
                merchant_context,
                merchant_connector_account_type,
                connector_customer_id,
                connector_name,
                key1,
                api_secret,
            )
            .await
        }
        _ => {
            router_env::logger::warn!(
                "External vault session creation is not supported for connector: {}",
                connector_name
            );
            Ok(None)
        }
    }
}


impl api::RefundExecute for Thunes {}


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


pub struct TsysPaymentAuthSaleRequest {
    #[serde(rename = "deviceID")]
    device_id: Secret<String>,
    transaction_key: Secret<String>,
    card_data_source: String,
    transaction_amount: StringMinorUnit,
    currency_code: enums::Currency,
    card_number: cards::CardNumber,
    expiration_date: Secret<String>,
    cvv2: Secret<String>,
    terminal_capability: String,
    terminal_operating_environment: String,
    cardholder_authentication_method: String,
    #[serde(rename = "developerID")]
    developer_id: Secret<String>,
    order_number: String,
}


pub fn is_refund_event(event: &Shift4WebhookEvent) -> bool {
    matches!(event, Shift4WebhookEvent::ChargeRefunded)
}


pub async fn payouts_list_by_filter_profile() {}


pub struct Authentication {
    pub authentication_id: common_utils::id_type::AuthenticationId,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub authentication_connector: Option<String>,
    pub connector_authentication_id: Option<String>,
    pub authentication_data: Option<Value>,
    pub payment_method_id: String,
    pub authentication_type: Option<common_enums::DecoupledAuthenticationType>,
    pub authentication_status: common_enums::AuthenticationStatus,
    pub authentication_lifecycle_status: common_enums::AuthenticationLifecycleStatus,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: time::PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: time::PrimitiveDateTime,
    pub error_message: Option<String>,
    pub error_code: Option<String>,
    pub connector_metadata: Option<Value>,
    pub maximum_supported_version: Option<common_utils::types::SemanticVersion>,
    pub threeds_server_transaction_id: Option<String>,
    pub cavv: Option<String>,
    pub authentication_flow_type: Option<String>,
    pub message_version: Option<common_utils::types::SemanticVersion>,
    pub eci: Option<String>,
    pub trans_status: Option<common_enums::TransactionStatus>,
    pub acquirer_bin: Option<String>,
    pub acquirer_merchant_id: Option<String>,
    pub three_ds_method_data: Option<String>,
    pub three_ds_method_url: Option<String>,
    pub acs_url: Option<String>,
    pub challenge_request: Option<String>,
    pub acs_reference_number: Option<String>,
    pub acs_trans_id: Option<String>,
    pub acs_signed_content: Option<String>,
    pub profile_id: common_utils::id_type::ProfileId,
    pub payment_id: Option<common_utils::id_type::PaymentId>,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub ds_trans_id: Option<String>,
    pub directory_server_id: Option<String>,
    pub acquirer_country_code: Option<String>,
    pub service_details: Option<Value>,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub authentication_client_secret: Option<String>,
    pub force_3ds_challenge: Option<bool>,
    pub psd2_sca_exemption_type: Option<common_enums::ScaExemptionType>,
    pub return_url: Option<String>,
    pub amount: Option<common_utils::types::MinorUnit>,
    pub currency: Option<common_enums::Currency>,
    #[encrypt(ty = Value)]
    pub billing_address: Option<Encryptable<crate::address::Address>>,
    #[encrypt(ty = Value)]
    pub shipping_address: Option<Encryptable<crate::address::Address>>,
    pub browser_info: Option<Value>,
    pub email: Option<Encryptable<Secret<String, pii::EmailStrategy>>>,
}


pub async fn contract_based_routing_update_configs() {}


impl RequestPaymentMethodTypes {
    /// Get payment_method_type
    #[cfg(feature = "v1")]
    pub fn get_payment_method_type(&self) -> Option<api_enums::PaymentMethodType> {
        Some(self.payment_method_type)
    }
}


Documentation: crates/storage_impl/README.md
# Type: Doc File

# Storage implementations

Storage backend implementations for data structures & objects.


pub struct FileMetadataNew {
    pub file_id: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub file_name: Option<String>,
    pub file_size: i32,
    pub file_type: String,
    pub provider_file_id: Option<String>,
    pub file_upload_provider: Option<common_enums::FileUploadProvider>,
    pub available: bool,
    pub connector_label: Option<String>,
    pub profile_id: Option<common_utils::id_type::ProfileId>,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
}


pub struct PlacetopayCard {
    number: cards::CardNumber,
    expiration: Secret<String>,
    cvv: Secret<String>,
}


impl api::ConnectorAccessToken for Trustpayments {}

impl api::ConnectorAccessToken for for Trustpayments


impl Responder {
    let flow = Flow::ApiKeyRevoke;
    let key_id = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        &key_id,
        |state,
         auth::AuthenticationDataWithoutProfile {
             merchant_account, ..
         },
         key_id,
         _| api_keys::revoke_api_key(state, merchant_account.get_id().to_owned(), key_id),
        auth::auth_type(
            &auth::AdminApiAuthWithMerchantIdFromHeader,
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantApiKeyWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


OpenAPI Block Path: components.schemas.ThreeDsDecisionRuleExecuteResponse
{
  "type": "object",
  "description": "Represents the response from executing a 3DS decision rule.",
  "required": [
    "decision"
  ],
  "properties": {
    "decision": {
      "$ref": "#/components/schemas/ThreeDSDecision"
    }
  }
}


impl Ebanx {
    pub fn new() -> &'static Self {
        &Self {
            #[cfg(feature = "payouts")]
            amount_converter: &FloatMajorUnitForConnector,
        }
    }
}


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

pub mod transformers;

use std::sync::LazyLock;

use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentsResponseData, RefundsResponseData, SupportedPaymentMethods,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use masking::{ExposeInterface, Mask};
use transformers as trustpayments;

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

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

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

impl api::Payment for Trustpayments {}
impl api::PaymentSession for Trustpayments {}
impl api::ConnectorAccessToken for Trustpayments {}
impl api::MandateSetup for Trustpayments {}
impl api::PaymentAuthorize for Trustpayments {}
impl api::PaymentSync for Trustpayments {}
impl api::PaymentCapture for Trustpayments {}
impl api::PaymentVoid for Trustpayments {}
impl api::Refund for Trustpayments {}
impl api::RefundExecute for Trustpayments {}
impl api::RefundSync for Trustpayments {}
impl api::PaymentToken for Trustpayments {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Trustpayments
{
    // Not Implemented (R)
}

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

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

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        // todo!()
        api::CurrencyUnit::Minor

        //    TODO! Check connector documentation, on which unit they are processing the currency.
        //    If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor,
        //    if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base
    }

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

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = trustpayments::TrustpaymentsAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::AUTHORIZATION.to_string(),
            auth.api_key.expose().into_masked(),
        )])
    }

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

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

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

impl ConnectorValidation for Trustpayments {
    fn validate_mandate_payment(
        &self,
        _pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        match pm_data {
            PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented(
                "validate_mandate_payment does not support cards".to_string(),
            )
            .into()),
            _ => Ok(()),
        }
    }

    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        Ok(())
    }
}

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

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

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

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData>
    for Trustpayments
{
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

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

        let connector_router_data = trustpayments::TrustpaymentsRouterData::from((amount, req));
        let connector_req =
            trustpayments::TrustpaymentsPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

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

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

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Trustpayments {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

    fn get_url(
        &self,
        _req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

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

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

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

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Trustpayments {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
    }

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

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

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

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

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Trustpayments {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

    fn get_url(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let refund_amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_refund_amount,
            req.request.currency,
        )?;

        let connector_router_data =
            trustpayments::TrustpaymentsRouterData::from((refund_amount, req));
        let connector_req =
            trustpayments::TrustpaymentsRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

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

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

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Trustpayments {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

    fn get_url(
        &self,
        _req: &RefundSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

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

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

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

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

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

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

static TRUSTPAYMENTS_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(SupportedPaymentMethods::new);

static TRUSTPAYMENTS_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Trustpayments",
    description: "Trustpayments connector",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Alpha,
};

static TRUSTPAYMENTS_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = [];

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

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

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


pub struct ElavonSyncResponse {
    pub ssl_trans_status: TransactionSyncStatus,
    pub ssl_transaction_type: SyncTransactionType,
    pub ssl_txn_id: String,
}


OpenAPI Block Path: components.schemas.BankDebitResponse
{
  "allOf": [
    {
      "allOf": [
        {
          "$ref": "#/components/schemas/BankDebitAdditionalData"
        }
      ],
      "nullable": true
    },
    {
      "type": "object"
    }
  ]
}


impl api::PaymentVoid for Zen {}