File: crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
 Public functions: 3
 Public structs: 26

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

use common_enums::enums;
use common_utils::{errors::CustomResult, ext_traits::ByteSliceExt, pii, types::MinorUnit};
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},
        RecoveryRecordBack,
    },
    router_request_types::{revenue_recovery::RevenueRecoveryRecordBackRequest, ResponseId},
    router_response_types::{
        revenue_recovery::RevenueRecoveryRecordBackResponse, PaymentsResponseData,
        RefundsResponseData,
    },
    types::{PaymentsAuthorizeRouterData, RefundsRouterData, RevenueRecoveryRecordBackRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

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

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

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

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

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

impl TryFrom<&ChargebeeRouterData<&PaymentsAuthorizeRouterData>> for ChargebeePaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &ChargebeeRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                let card = ChargebeeCard {
                    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,
                    card,
                })
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

// Auth Struct
pub struct ChargebeeAuthType {
    pub(super) full_access_key_v1: Secret<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ChargebeeMetadata {
    pub(super) site: Secret<String>,
}

impl TryFrom<&Option<pii::SecretSerdeValue>> for ChargebeeMetadata {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(meta_data: &Option<pii::SecretSerdeValue>) -> Result<Self, Self::Error> {
        let metadata: Self = utils::to_connector_meta_from_secret::<Self>(meta_data.clone())
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "metadata",
            })?;
        Ok(metadata)
    }
}

impl TryFrom<&ConnectorAuthType> for ChargebeeAuthType {
    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 {
                full_access_key_v1: api_key.clone(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}
// PaymentsResponse
//TODO: Append the remaining status flags
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum ChargebeePaymentStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

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

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

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

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

// Type definition for Refund Response

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

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

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

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

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

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct ChargebeeErrorResponse {
    pub api_error_code: String,
    pub message: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeWebhookBody {
    pub content: ChargebeeWebhookContent,
    pub event_type: ChargebeeEventType,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeInvoiceBody {
    pub content: ChargebeeInvoiceContent,
    pub event_type: ChargebeeEventType,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeInvoiceContent {
    pub invoice: ChargebeeInvoiceData,
    pub subscription: Option<ChargebeeSubscriptionData>,
}

#[derive(Serialize, Deserialize, Debug)]

pub struct ChargebeeWebhookContent {
    pub transaction: ChargebeeTransactionData,
    pub invoice: ChargebeeInvoiceData,
    pub customer: Option<ChargebeeCustomer>,
    pub subscription: Option<ChargebeeSubscriptionData>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeSubscriptionData {
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub current_term_start: Option<PrimitiveDateTime>,
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub next_billing_at: Option<PrimitiveDateTime>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ChargebeeEventType {
    PaymentSucceeded,
    PaymentFailed,
    InvoiceDeleted,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeInvoiceData {
    // invoice id
    pub id: String,
    pub total: MinorUnit,
    pub currency_code: enums::Currency,
    pub billing_address: Option<ChargebeeInvoiceBillingAddress>,
    pub linked_payments: Option<Vec<ChargebeeInvoicePayments>>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeInvoicePayments {
    pub txn_status: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeTransactionData {
    id_at_gateway: Option<String>,
    status: ChargebeeTranasactionStatus,
    error_code: Option<String>,
    error_text: Option<String>,
    gateway_account_id: String,
    currency_code: enums::Currency,
    amount: MinorUnit,
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    date: Option<PrimitiveDateTime>,
    payment_method: ChargebeeTransactionPaymentMethod,
    payment_method_details: String,
}

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

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeePaymentMethodDetails {
    card: ChargebeeCardDetails,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeCardDetails {
    funding_type: ChargebeeFundingType,
    brand: common_enums::CardNetwork,
    iin: String,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ChargebeeFundingType {
    Credit,
    Debit,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ChargebeeTranasactionStatus {
    // Waiting for response from the payment gateway.
    InProgress,
    // The transaction is successful.
    Success,
    // Transaction failed.
    Failure,
    // No response received while trying to charge the card.
    Timeout,
    // Indicates that a successful payment transaction has failed now due to a late failure notification from the payment gateway,
    // typically caused by issues like insufficient funds or a closed bank account.
    LateFailure,
    // Connection with Gateway got terminated abruptly. So, status of this transaction needs to be resolved manually
    NeedsAttention,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeCustomer {
    pub payment_method: ChargebeePaymentMethod,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeeInvoiceBillingAddress {
    pub line1: Option<Secret<String>>,
    pub line2: Option<Secret<String>>,
    pub line3: Option<Secret<String>>,
    pub state: Option<Secret<String>>,
    pub country: Option<enums::CountryAlpha2>,
    pub zip: Option<Secret<String>>,
    pub city: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ChargebeePaymentMethod {
    pub reference_id: String,
    pub gateway: ChargebeeGateway,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ChargebeeGateway {
    Stripe,
    Braintree,
}

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

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

pub struct ChargebeeMandateDetails {
    pub customer_id: String,
    pub mandate_id: String,
}

impl ChargebeeCustomer {
    // the logic to find connector customer id & mandate id is different for different gateways, reference : https://apidocs.chargebee.com/docs/api/customers?prod_cat_ver=2#customer_payment_method_reference_id .
    pub fn find_connector_ids(&self) -> Result<ChargebeeMandateDetails, errors::ConnectorError> {
        match self.payment_method.gateway {
            ChargebeeGateway::Stripe | ChargebeeGateway::Braintree => {
                let mut parts = self.payment_method.reference_id.split('/');
                let customer_id = parts
                    .next()
                    .ok_or(errors::ConnectorError::WebhookBodyDecodingFailed)?
                    .to_string();
                let mandate_id = parts
                    .next_back()
                    .ok_or(errors::ConnectorError::WebhookBodyDecodingFailed)?
                    .to_string();
                Ok(ChargebeeMandateDetails {
                    customer_id,
                    mandate_id,
                })
            }
        }
    }
}

#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
impl TryFrom<ChargebeeWebhookBody> for revenue_recovery::RevenueRecoveryAttemptData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: ChargebeeWebhookBody) -> Result<Self, Self::Error> {
        let amount = item.content.transaction.amount;
        let currency = item.content.transaction.currency_code.to_owned();
        let merchant_reference_id =
            common_utils::id_type::PaymentReferenceId::from_str(&item.content.invoice.id)
                .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let connector_transaction_id = item
            .content
            .transaction
            .id_at_gateway
            .map(common_utils::types::ConnectorTransactionId::TxnId);
        let error_code = item.content.transaction.error_code.clone();
        let error_message = item.content.transaction.error_text.clone();
        let connector_mandate_details = item
            .content
            .customer
            .as_ref()
            .map(|customer| customer.find_connector_ids())
            .transpose()?
            .ok_or(errors::ConnectorError::MissingRequiredField {
                field_name: "connector_mandate_details",
            })?;
        let connector_account_reference_id = item.content.transaction.gateway_account_id.clone();
        let transaction_created_at = item.content.transaction.date;
        let status = enums::AttemptStatus::from(item.content.transaction.status);
        let payment_method_type =
            enums::PaymentMethod::from(item.content.transaction.payment_method);
        let payment_method_details: ChargebeePaymentMethodDetails =
            serde_json::from_str(&item.content.transaction.payment_method_details)
                .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let payment_method_sub_type =
            enums::PaymentMethodType::from(payment_method_details.card.funding_type);
        // Chargebee retry count will always be less than u16 always. Chargebee can have maximum 12 retry attempts
        #[allow(clippy::as_conversions)]
        let retry_count = item
            .content
            .invoice
            .linked_payments
            .map(|linked_payments| linked_payments.len() as u16);
        let invoice_next_billing_time = item
            .content
            .subscription
            .as_ref()
            .and_then(|subscription| subscription.next_billing_at);
        let invoice_billing_started_at_time = item
            .content
            .subscription
            .as_ref()
            .and_then(|subscription| subscription.current_term_start);
        Ok(Self {
            amount,
            currency,
            merchant_reference_id,
            connector_transaction_id,
            error_code,
            error_message,
            processor_payment_method_token: connector_mandate_details.mandate_id,
            connector_customer_id: connector_mandate_details.customer_id,
            connector_account_reference_id,
            transaction_created_at,
            status,
            payment_method_type,
            payment_method_sub_type,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            retry_count,
            invoice_next_billing_time,
            invoice_billing_started_at_time,
            // This field is none because it is specific to stripebilling.
            charge_id: None,
            // Need to populate these card info field
            card_info: api_models::payments::AdditionalCardInfo {
                card_network: Some(payment_method_details.card.brand),
                card_isin: Some(payment_method_details.card.iin),
                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,
            },
        })
    }
}

impl From<ChargebeeTranasactionStatus> for enums::AttemptStatus {
    fn from(status: ChargebeeTranasactionStatus) -> Self {
        match status {
            ChargebeeTranasactionStatus::InProgress
            | ChargebeeTranasactionStatus::NeedsAttention => Self::Pending,
            ChargebeeTranasactionStatus::Success => Self::Charged,
            ChargebeeTranasactionStatus::Failure
            | ChargebeeTranasactionStatus::Timeout
            | ChargebeeTranasactionStatus::LateFailure => Self::Failure,
        }
    }
}

impl From<ChargebeeTransactionPaymentMethod> for enums::PaymentMethod {
    fn from(payment_method: ChargebeeTransactionPaymentMethod) -> Self {
        match payment_method {
            ChargebeeTransactionPaymentMethod::Card => Self::Card,
        }
    }
}

impl From<ChargebeeFundingType> for enums::PaymentMethodType {
    fn from(funding_type: ChargebeeFundingType) -> Self {
        match funding_type {
            ChargebeeFundingType::Credit => Self::Credit,
            ChargebeeFundingType::Debit => Self::Debit,
        }
    }
}
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
impl From<ChargebeeEventType> for api_models::webhooks::IncomingWebhookEvent {
    fn from(event: ChargebeeEventType) -> Self {
        match event {
            ChargebeeEventType::PaymentSucceeded => Self::RecoveryPaymentSuccess,
            ChargebeeEventType::PaymentFailed => Self::RecoveryPaymentFailure,
            ChargebeeEventType::InvoiceDeleted => Self::RecoveryInvoiceCancel,
        }
    }
}

#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
impl TryFrom<ChargebeeInvoiceBody> for revenue_recovery::RevenueRecoveryInvoiceData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: ChargebeeInvoiceBody) -> Result<Self, Self::Error> {
        let merchant_reference_id =
            common_utils::id_type::PaymentReferenceId::from_str(&item.content.invoice.id)
                .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        // The retry count will never exceed u16 limit in a billing connector. It can have maximum of 12 in case of charge bee so its ok to suppress this
        #[allow(clippy::as_conversions)]
        let retry_count = item
            .content
            .invoice
            .linked_payments
            .as_ref()
            .map(|linked_payments| linked_payments.len() as u16);
        let invoice_next_billing_time = item
            .content
            .subscription
            .as_ref()
            .and_then(|subscription| subscription.next_billing_at);
        let billing_started_at = item
            .content
            .subscription
            .as_ref()
            .and_then(|subscription| subscription.current_term_start);
        Ok(Self {
            amount: item.content.invoice.total,
            currency: item.content.invoice.currency_code,
            merchant_reference_id,
            billing_address: Some(api_models::payments::Address::from(item.content.invoice)),
            retry_count,
            next_billing_at: invoice_next_billing_time,
            billing_started_at,
        })
    }
}

#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
impl From<ChargebeeInvoiceData> for api_models::payments::Address {
    fn from(item: ChargebeeInvoiceData) -> Self {
        Self {
            address: item
                .billing_address
                .map(api_models::payments::AddressDetails::from),
            phone: None,
            email: None,
        }
    }
}

#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
impl From<ChargebeeInvoiceBillingAddress> for api_models::payments::AddressDetails {
    fn from(item: ChargebeeInvoiceBillingAddress) -> Self {
        Self {
            city: item.city,
            country: item.country,
            state: item.state,
            zip: item.zip,
            line1: item.line1,
            line2: item.line2,
            line3: item.line3,
            first_name: None,
            last_name: None,
            origin_zip: None,
        }
    }
}

#[derive(Debug, Serialize)]
pub struct ChargebeeRecordPaymentRequest {
    #[serde(rename = "transaction[amount]")]
    pub amount: MinorUnit,
    #[serde(rename = "transaction[payment_method]")]
    pub payment_method: ChargebeeRecordPaymentMethod,
    #[serde(rename = "transaction[id_at_gateway]")]
    pub connector_payment_id: Option<String>,
    #[serde(rename = "transaction[status]")]
    pub status: ChargebeeRecordStatus,
}

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

#[derive(Debug, Serialize, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum ChargebeeRecordStatus {
    Success,
    Failure,
}

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
impl TryFrom<&ChargebeeRouterData<&RevenueRecoveryRecordBackRouterData>>
    for ChargebeeRecordPaymentRequest
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &ChargebeeRouterData<&RevenueRecoveryRecordBackRouterData>,
    ) -> Result<Self, Self::Error> {
        let req = &item.router_data.request;
        Ok(Self {
            amount: req.amount,
            payment_method: ChargebeeRecordPaymentMethod::Other,
            connector_payment_id: req
                .connector_transaction_id
                .as_ref()
                .map(|connector_payment_id| connector_payment_id.get_id().to_string()),
            status: ChargebeeRecordStatus::try_from(req.attempt_status)?,
        })
    }
}

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
impl TryFrom<enums::AttemptStatus> for ChargebeeRecordStatus {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(status: enums::AttemptStatus) -> Result<Self, Self::Error> {
        match status {
            enums::AttemptStatus::Charged
            | enums::AttemptStatus::PartialCharged
            | enums::AttemptStatus::PartialChargedAndChargeable => Ok(Self::Success),
            enums::AttemptStatus::Failure
            | enums::AttemptStatus::CaptureFailed
            | enums::AttemptStatus::RouterDeclined => Ok(Self::Failure),
            enums::AttemptStatus::AuthenticationFailed
            | enums::AttemptStatus::Started
            | enums::AttemptStatus::AuthenticationPending
            | enums::AttemptStatus::AuthenticationSuccessful
            | enums::AttemptStatus::Authorized
            | enums::AttemptStatus::PartiallyAuthorized
            | enums::AttemptStatus::AuthorizationFailed
            | enums::AttemptStatus::Authorizing
            | enums::AttemptStatus::CodInitiated
            | enums::AttemptStatus::Voided
            | enums::AttemptStatus::VoidedPostCharge
            | enums::AttemptStatus::VoidInitiated
            | enums::AttemptStatus::CaptureInitiated
            | enums::AttemptStatus::VoidFailed
            | enums::AttemptStatus::AutoRefunded
            | enums::AttemptStatus::Unresolved
            | enums::AttemptStatus::Pending
            | enums::AttemptStatus::PaymentMethodAwaited
            | enums::AttemptStatus::ConfirmationAwaited
            | enums::AttemptStatus::DeviceDataCollectionPending
            | enums::AttemptStatus::IntegrityFailure
            | enums::AttemptStatus::Expired => Err(errors::ConnectorError::NotSupported {
                message: "Record back flow is only supported for terminal status".to_string(),
                connector: "chargebee",
            }
            .into()),
        }
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ChargebeeRecordbackResponse {
    pub invoice: ChargebeeRecordbackInvoice,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ChargebeeRecordbackInvoice {
    pub id: common_utils::id_type::PaymentReferenceId,
}

impl
    TryFrom<
        ResponseRouterData<
            RecoveryRecordBack,
            ChargebeeRecordbackResponse,
            RevenueRecoveryRecordBackRequest,
            RevenueRecoveryRecordBackResponse,
        >,
    > for RevenueRecoveryRecordBackRouterData
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            RecoveryRecordBack,
            ChargebeeRecordbackResponse,
            RevenueRecoveryRecordBackRequest,
            RevenueRecoveryRecordBackResponse,
        >,
    ) -> Result<Self, Self::Error> {
        let merchant_reference_id = item.response.invoice.id;
        Ok(Self {
            response: Ok(RevenueRecoveryRecordBackResponse {
                merchant_reference_id,
            }),
            ..item.data
        })
    }
}


File: crates/openapi/src/routes.rs

#![allow(unused)]

pub mod api_keys;
pub mod authentication;
pub mod blocklist;
pub mod customers;
pub mod disputes;
pub mod gsm;
pub mod mandates;
pub mod merchant_account;
pub mod merchant_connector_account;
pub mod organization;
pub mod payment_link;
pub mod payment_method;
pub mod payments;
pub mod payouts;
pub mod platform;
pub mod poll;
pub mod profile;
pub mod profile_acquirer;
pub mod proxy;
pub mod refunds;
pub mod relay;
pub mod revenue_recovery;
pub mod routing;
pub mod three_ds_decision_rule;
pub mod tokenization;
pub mod webhook_events;


pub fn get_trustpay_response(
    response: TrustpayPaymentsResponse,
    status_code: u16,
    previous_attempt_status: enums::AttemptStatus,
) -> CustomResult<
    (
        enums::AttemptStatus,
        Option<ErrorResponse>,
        PaymentsResponseData,
    ),
    errors::ConnectorError,
> {
    match response {
        TrustpayPaymentsResponse::CardsPayments(response) => {
            handle_cards_response(*response, status_code)
        }
        TrustpayPaymentsResponse::BankRedirectPayments(response) => {
            handle_bank_redirects_response(*response)
        }
        TrustpayPaymentsResponse::BankRedirectSync(response) => {
            handle_bank_redirects_sync_response(*response, status_code)
        }
        TrustpayPaymentsResponse::BankRedirectError(response) => {
            handle_bank_redirects_error_response(*response, status_code, previous_attempt_status)
        }
        TrustpayPaymentsResponse::WebhookResponse(response) => {
            handle_webhook_response(*response, status_code)
        }
    }
}


pub struct GpayInfo {
    pub payment_token: Option<Secret<String>>,
}


pub struct EbanxFulfillResponse {
    #[serde(rename = "type")]
    status: EbanxFulfillStatus,
    message: String,
}


pub struct MerchantConnectorWebhookDetails {
    #[schema(value_type = String, example = "12345678900987654321")]
    pub merchant_secret: Secret<String>,
    #[schema(value_type = String, example = "12345678900987654321")]
    pub additional_secret: Option<Secret<String>>,
}


impl Barclaycard {
    pub fn generate_digest(&self, payload: &[u8]) -> String {
        let payload_digest = digest::digest(&digest::SHA256, payload);
        consts::BASE64_ENGINE.encode(payload_digest)
    }

    pub fn generate_signature(
        &self,
        auth: barclaycard::BarclaycardAuthType,
        host: String,
        resource: &str,
        payload: &String,
        date: OffsetDateTime,
        http_method: Method,
    ) -> CustomResult<String, errors::ConnectorError> {
        let barclaycard::BarclaycardAuthType {
            api_key,
            merchant_account,
            api_secret,
        } = auth;
        let is_post_method = matches!(http_method, Method::Post);
        let digest_str = if is_post_method { "digest " } else { "" };
        let headers = format!("host date (request-target) {digest_str}{V_C_MERCHANT_ID}");
        let request_target = if is_post_method {
            format!("(request-target): post {resource}\ndigest: SHA-256={payload}\n")
        } else {
            format!("(request-target): get {resource}\n")
        };
        let signature_string = format!(
            "host: {host}\ndate: {date}\n{request_target}{V_C_MERCHANT_ID}: {}",
            merchant_account.peek()
        );
        let key_value = consts::BASE64_ENGINE
            .decode(api_secret.expose())
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "connector_account_details.api_secret",
            })?;
        let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value);
        let signature_value =
            consts::BASE64_ENGINE.encode(hmac::sign(&key, signature_string.as_bytes()).as_ref());
        let signature_header = format!(
            r#"keyid="{}", algorithm="HmacSHA256", headers="{headers}", signature="{signature_value}""#,
            api_key.peek()
        );

        Ok(signature_header)
    }
}


pub struct StoredCredentials {
    pub sequence: Sequence,
    pub initiator: Initiator,
    pub is_scheduled: bool,
    pub cardbrand_original_transaction_id: Option<Secret<String>>,
}


pub struct PayuAuthUpdateRequest {
    grant_type: String,
    client_id: Secret<String>,
    client_secret: Secret<String>,
}


pub struct PaymentAddress {
    shipping: Option<Address>,
    billing: Option<Address>,
    unified_payment_method_billing: Option<Address>,
    payment_method_billing: Option<Address>,
}


pub fn new(time_bucket: Option<String>) -> Self {
        Self { time_bucket }
    }


pub async fn switch_merchant_for_user_in_org(
    state: SessionState,
    request: user_api::SwitchMerchantRequest,
    user_from_token: auth::UserFromToken,
) -> UserResponse<user_api::TokenResponse> {
    if user_from_token.merchant_id == request.merchant_id {
        return Err(UserErrors::InvalidRoleOperationWithMessage(
            "User switching to same merchant".to_string(),
        )
        .into());
    }

    let key_manager_state = &(&state).into();
    let role_info = roles::RoleInfo::from_role_id_org_id_tenant_id(
        &state,
        &user_from_token.role_id,
        &user_from_token.org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await
    .change_context(UserErrors::InternalServerError)
    .attach_printable("Failed to retrieve role information")?;

    // Check if the role is internal and handle separately
    let (org_id, merchant_id, profile_id, role_id) = if role_info.is_internal() {
        let merchant_key_store = state
            .store
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &request.merchant_id,
                &state.store.get_master_key().to_vec().into(),
            )
            .await
            .to_not_found_response(UserErrors::MerchantIdNotFound)?;

        let merchant_account = state
            .store
            .find_merchant_account_by_merchant_id(
                key_manager_state,
                &request.merchant_id,
                &merchant_key_store,
            )
            .await
            .to_not_found_response(UserErrors::MerchantIdNotFound)?;

        let profile_id = state
            .store
            .list_profile_by_merchant_id(
                key_manager_state,
                &merchant_key_store,
                &request.merchant_id,
            )
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Failed to list business profiles by merchant_id")?
            .pop()
            .ok_or(UserErrors::InternalServerError)
            .attach_printable("No business profile found for the given merchant_id")?
            .get_id()
            .to_owned();

        (
            merchant_account.organization_id,
            request.merchant_id,
            profile_id,
            user_from_token.role_id.clone(),
        )
    } else {
        // Match based on the other entity types
        match role_info.get_entity_type() {
            EntityType::Tenant | EntityType::Organization => {
                let merchant_key_store = state
                    .store
                    .get_merchant_key_store_by_merchant_id(
                        key_manager_state,
                        &request.merchant_id,
                        &state.store.get_master_key().to_vec().into(),
                    )
                    .await
                    .to_not_found_response(UserErrors::MerchantIdNotFound)?;

                let merchant_id = state
                    .store
                    .find_merchant_account_by_merchant_id(
                        key_manager_state,
                        &request.merchant_id,
                        &merchant_key_store,
                    )
                    .await
                    .change_context(UserErrors::MerchantIdNotFound)?
                    .organization_id
                    .eq(&user_from_token.org_id)
                    .then(|| request.merchant_id.clone())
                    .ok_or_else(|| {
                        UserErrors::InvalidRoleOperationWithMessage(
                            "No such merchant_id found for the user in the org".to_string(),
                        )
                    })?;

                let profile_id = state
                    .store
                    .list_profile_by_merchant_id(
                        key_manager_state,
                        &merchant_key_store,
                        &merchant_id,
                    )
                    .await
                    .change_context(UserErrors::InternalServerError)
                    .attach_printable("Failed to list business profiles by merchant_id")?
                    .pop()
                    .ok_or(UserErrors::InternalServerError)
                    .attach_printable("No business profile found for the merchant_id")?
                    .get_id()
                    .to_owned();
                (
                    user_from_token.org_id.clone(),
                    merchant_id,
                    profile_id,
                    user_from_token.role_id.clone(),
                )
            }

            EntityType::Merchant | EntityType::Profile => {
                let user_role = state
                    .global_store
                    .list_user_roles_by_user_id(ListUserRolesByUserIdPayload {
                        user_id: &user_from_token.user_id,
                        tenant_id: user_from_token
                            .tenant_id
                            .as_ref()
                            .unwrap_or(&state.tenant.tenant_id),
                        org_id: Some(&user_from_token.org_id),
                        merchant_id: Some(&request.merchant_id),
                        profile_id: None,
                        entity_id: None,
                        version: None,
                        status: Some(UserStatus::Active),
                        limit: Some(1),
                    })
                    .await
                    .change_context(UserErrors::InternalServerError)
                    .attach_printable(
                        "Failed to list user roles for the given user_id, org_id and merchant_id",
                    )?
                    .pop()
                    .ok_or(UserErrors::InvalidRoleOperationWithMessage(
                        "No user role associated with the requested merchant_id".to_string(),
                    ))?;

                let (merchant_id, profile_id) =
                    utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role)
                        .await?;
                (
                    user_from_token.org_id,
                    merchant_id,
                    profile_id,
                    user_role.role_id,
                )
            }
        }
    };

    let lineage_context = LineageContext {
        user_id: user_from_token.user_id.clone(),
        merchant_id: merchant_id.clone(),
        role_id: role_id.clone(),
        org_id: org_id.clone(),
        profile_id: profile_id.clone(),
        tenant_id: user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id)
            .clone(),
    };

    utils::user::spawn_async_lineage_context_update_to_db(
        &state,
        &user_from_token.user_id,
        lineage_context,
    );

    let token = utils::user::generate_jwt_auth_token_with_attributes(
        &state,
        user_from_token.user_id,
        merchant_id.clone(),
        org_id.clone(),
        role_id.clone(),
        profile_id,
        user_from_token.tenant_id.clone(),
    )
    .await?;

    utils::user_role::set_role_info_in_cache_by_role_id_org_id(
        &state,
        &role_id,
        &org_id,
        user_from_token
            .tenant_id
            .as_ref()
            .unwrap_or(&state.tenant.tenant_id),
    )
    .await;

    let response = user_api::TokenResponse {
        token: token.clone(),
        token_type: common_enums::TokenPurpose::UserInfo,
    };

    auth::cookies::set_cookie_response(response, token)
}


impl api::RefundSync for Authipay {}


impl api::PaymentSync for Bambora {}


pub struct AttributeResponse {
    vault: PaypalVaultResponse,
}


impl ConnectorSpecifications for Vgs {}

impl ConnectorSpecifications for for Vgs


pub fn build(self) -> api::CardNetworkTokenizeResponse {
        api::CardNetworkTokenizeResponse {
            payment_method_response: self.payment_method_response,
            customer: self.customer.cloned(),
            card_tokenized: self.card_tokenized,
            error_code: self.error_code.cloned(),
            error_message: self.error_message.cloned(),
            // Below field is mutated by caller functions for batched API operations
            tokenization_data: None,
        }
    }


pub struct PanMetadataUpdateBody {
    pub card: NetworkTokenRequestorData,
}


impl Address {
    /// Unify the address, giving priority to `self` when details are present in both
    pub fn unify_address(&self, other: Option<&Self>) -> Self {
        let other_address_details = other.and_then(|address| address.address.as_ref());
        Self {
            address: self
                .address
                .as_ref()
                .map(|address| address.unify_address_details(other_address_details))
                .or(other_address_details.cloned()),
            email: self
                .email
                .clone()
                .or(other.and_then(|other| other.email.clone())),
            phone: {
                self.phone
                    .clone()
                    .and_then(|phone_details| {
                        if phone_details.number.is_some() {
                            Some(phone_details)
                        } else {
                            None
                        }
                    })
                    .or_else(|| other.and_then(|other| other.phone.clone()))
            },
        }
    }
}


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

use std::str::FromStr;

use common_utils::pii::Email;
use hyperswitch_domain_models::address::{Address, AddressDetails, PhoneDetails};
use masking::Secret;
use router::types::{self, api, domain, storage::enums};

use crate::{
    connector_auth,
    utils::{self, ConnectorActions, PaymentAuthorizeType},
};
struct Cybersource;
impl ConnectorActions for Cybersource {}
impl utils::Connector for Cybersource {
    fn get_data(&self) -> api::ConnectorData {
        use router::connector::Cybersource;
        utils::construct_connector_data_old(
            Box::new(Cybersource::new()),
            types::Connector::Cybersource,
            api::GetToken::Connector,
            None,
        )
    }
    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .cybersource
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }
    fn get_name(&self) -> String {
        "cybersource".to_string()
    }
}

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    Some(utils::PaymentInfo {
        address: Some(types::PaymentAddress::new(
            None,
            Some(Address {
                address: Some(AddressDetails {
                    first_name: Some(Secret::new("first".to_string())),
                    last_name: Some(Secret::new("last".to_string())),
                    line1: Some(Secret::new("line1".to_string())),
                    line2: Some(Secret::new("line2".to_string())),
                    city: Some("city".to_string()),
                    zip: Some(Secret::new("zip".to_string())),
                    country: Some(api_models::enums::CountryAlpha2::IN),
                    ..Default::default()
                }),
                phone: Some(PhoneDetails {
                    number: Some(Secret::new("9123456789".to_string())),
                    country_code: Some("+91".to_string()),
                }),
                email: None,
            }),
            None,
            None,
        )),
        ..Default::default()
    })
}
fn get_default_payment_authorize_data() -> Option<types::PaymentsAuthorizeData> {
    Some(types::PaymentsAuthorizeData {
        currency: enums::Currency::USD,
        email: Some(Email::from_str("abc@gmail.com").unwrap()),
        ..PaymentAuthorizeType::default().0
    })
}
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = Cybersource {}
        .authorize_payment(
            get_default_payment_authorize_data(),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}
#[actix_web::test]
async fn should_make_payment() {
    let response = Cybersource {}
        .make_payment(
            get_default_payment_authorize_data(),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Pending);
}
#[actix_web::test]
async fn should_capture_already_authorized_payment() {
    let connector = Cybersource {};
    let response = connector
        .authorize_and_capture_payment(
            get_default_payment_authorize_data(),
            None,
            get_default_payment_info(),
        )
        .await;
    assert_eq!(response.unwrap().status, enums::AttemptStatus::Pending);
}
#[actix_web::test]
async fn should_partially_capture_already_authorized_payment() {
    let connector = Cybersource {};
    let response = connector
        .authorize_and_capture_payment(
            get_default_payment_authorize_data(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
    assert_eq!(response.unwrap().status, enums::AttemptStatus::Pending);
}

#[actix_web::test]
#[ignore = "Status field is missing in the response, Communication is being done with cybersource team"]
async fn should_sync_payment() {
    let connector = Cybersource {};
    let response = connector
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    "6699597903496176903954".to_string(),
                ),
                ..Default::default()
            }),
            None,
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}
#[actix_web::test]
async fn should_void_already_authorized_payment() {
    let connector = Cybersource {};
    let response = connector
        .authorize_and_void_payment(
            get_default_payment_authorize_data(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: "".to_string(),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await;
    assert_eq!(response.unwrap().status, enums::AttemptStatus::Voided);
}

#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = Cybersource {}
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_month: Secret::new("13".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..get_default_payment_authorize_data().unwrap()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let x = response.response.unwrap_err();
    assert_eq!(
        x.message,
        "Declined - One or more fields in the request contains invalid data",
    );
    assert_eq!(
        x.reason,
        Some("paymentInformation.card.expirationMonth : INVALID_DATA".to_string())
    );
}
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_year() {
    let response = Cybersource {}
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_year: Secret::new("2022".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..get_default_payment_authorize_data().unwrap()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let x = response.response.unwrap_err();
    assert_eq!(x.message, "Decline - Expired card. You might also receive this if the expiration date you provided does not match the date the issuing bank has on file.");
}
#[actix_web::test]
async fn should_fail_payment_for_invalid_card_cvc() {
    let response = Cybersource {}
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_cvc: Secret::new("9123456789".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..get_default_payment_authorize_data().unwrap()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let x = response.response.unwrap_err();
    assert_eq!(
        x.message,
        "Declined - One or more fields in the request contains invalid data",
    );
    assert_eq!(
        x.reason,
        Some("paymentInformation.card.securityCode : INVALID_DATA".to_string())
    );
}
// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_reversed_payment() {
    let connector = Cybersource {};
    // Authorize
    let authorize_response = connector
        .make_payment(
            get_default_payment_authorize_data(),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Pending);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    // Void
    let void_response = connector
        .void_payment("6736046645576085004953".to_string(), None, None)
        .await
        .unwrap();
    let res = void_response.response.unwrap_err();
    assert_eq!(
        res.message,
        "Decline - The authorization has already been reversed."
    );
}
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let connector = Cybersource {};
    let response = connector
        .capture_payment("12345".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    let err = response.response.unwrap_err();
    assert_eq!(
        err.message,
        "Declined - One or more fields in the request contains invalid data"
    );
    assert_eq!(err.code, "InvalidData".to_string());
}
#[actix_web::test]
async fn should_refund_succeeded_payment() {
    let connector = Cybersource {};
    let response = connector
        .make_payment_and_refund(
            get_default_payment_authorize_data(),
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Pending,
    );
}
#[actix_web::test]
#[ignore = "Connector Error, needs to be looked into and fixed"]
async fn should_refund_manually_captured_payment() {
    let connector = Cybersource {};
    let response = connector
        .auth_capture_and_refund(
            get_default_payment_authorize_data(),
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Pending,
    );
}
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let connector = Cybersource {};
    let refund_response = connector
        .make_payment_and_refund(
            get_default_payment_authorize_data(),
            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::Pending,
    );
}

#[actix_web::test]
#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."]
async fn should_partially_refund_manually_captured_payment() {
    let connector = Cybersource {};
    let response = connector
        .auth_capture_and_refund(
            get_default_payment_authorize_data(),
            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::Pending,
    );
}

#[actix_web::test]
async fn should_fail_refund_for_invalid_amount() {
    let connector = Cybersource {};
    let response = connector
        .make_payment_and_refund(
            get_default_payment_authorize_data(),
            Some(types::RefundsData {
                refund_amount: 15000,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Pending,
    );
}
#[actix_web::test]
#[ignore = "Status field is missing in the response, Communication is being done with cybersource team"]
async fn should_sync_refund() {
    let connector = Cybersource {};
    let response = connector
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            "6699597076726585603955".to_string(),
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

#[actix_web::test]
#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."]
async fn should_sync_manually_captured_refund() {}

#[actix_web::test]
#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."]
async fn should_refund_auto_captured_payment() {}

#[actix_web::test]
#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."]
async fn should_refund_succeeded_payment_multiple_times() {}

#[actix_web::test]
#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {}


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

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

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

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

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

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

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


impl api::RefundSync for Worldpayvantiv {}

impl api::RefundSync for for Worldpayvantiv


pub struct ElavonErrorResponse {
    error_code: Option<String>,
    error_message: String,
    error_name: String,
}


impl HashedApiKey {
    pub fn into_inner(self) -> String {
        self.0
    }
}


pub struct CardData {
    pub card_number: CardNumber,
    pub exp_month: Secret<String>,
    pub exp_year: Secret<String>,
    pub card_security_code: Option<Secret<String>>,
}


OpenAPI Block Path: paths."/events/{merchant_id}/{event_id}/retry"
{
  "post": {
    "tags": [
      "Event"
    ],
    "summary": "Events - Manual Retry",
    "description": "Manually retry the delivery of the specified Event.",
    "operationId": "Manually retry the delivery of an Event",
    "parameters": [
      {
        "name": "merchant_id",
        "in": "path",
        "description": "The unique identifier for the Merchant Account.",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      {
        "name": "event_id",
        "in": "path",
        "description": "The unique identifier for the Event",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    ],
    "responses": {
      "200": {
        "description": "The delivery of the Event was attempted. Check the `response` field in the response payload to identify the status of the delivery attempt.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/EventRetrieveResponse"
            }
          }
        }
      }
    },
    "security": [
      {
        "admin_api_key": []
      }
    ]
  }
}


/// get_requires_cvv_key
    pub fn get_requires_cvv_key(&self) -> String {
        format!("{}_requires_cvv", self.get_string_repr())
    }


pub async fn list_routing_configs_for_profile(
    state: web::Data<AppState>,
    req: HttpRequest,
    query: web::Query<RoutingRetrieveQuery>,
    transaction_type: Option<enums::TransactionType>,
) -> impl Responder {
    let flow = Flow::RoutingRetrieveDictionary;
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        query.into_inner(),
        |state, auth: auth::AuthenticationData, query_params, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            routing::retrieve_merchant_routing_dictionary(
                state,
                merchant_context,
                auth.profile_id.map(|profile_id| vec![profile_id]),
                query_params.clone(),
                transaction_type
                    .or(query_params.transaction_type)
                    .unwrap_or(enums::TransactionType::Payment),
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::ProfileRoutingRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


File: crates/hyperswitch_connectors/src/connectors/aci/aci_result_codes.rs

pub(super) const FAILURE_CODES: [&str; 501] = [
    "100.370.100",
    "100.370.110",
    "100.370.111",
    "100.380.100",
    "100.380.101",
    "100.380.110",
    "100.380.201",
    "100.380.305",
    "100.380.306",
    "100.380.401",
    "100.380.501",
    "100.395.502",
    "100.396.101",
    "100.400.000",
    "100.400.001",
    "100.400.002",
    "100.400.005",
    "100.400.007",
    "100.400.020",
    "100.400.021",
    "100.400.030",
    "100.400.039",
    "100.400.040",
    "100.400.041",
    "100.400.042",
    "100.400.043",
    "100.400.044",
    "100.400.045",
    "100.400.051",
    "100.400.060",
    "100.400.061",
    "100.400.063",
    "100.400.064",
    "100.400.065",
    "100.400.071",
    "100.400.080",
    "100.400.081",
    "100.400.083",
    "100.400.084",
    "100.400.085",
    "100.400.086",
    "100.400.087",
    "100.400.091",
    "100.400.100",
    "100.400.120",
    "100.400.121",
    "100.400.122",
    "100.400.123",
    "100.400.130",
    "100.400.139",
    "100.400.140",
    "100.400.141",
    "100.400.142",
    "100.400.143",
    "100.400.144",
    "100.400.145",
    "100.400.146",
    "100.400.147",
    "100.400.148",
    "100.400.149",
    "100.400.150",
    "100.400.151",
    "100.400.152",
    "100.400.241",
    "100.400.242",
    "100.400.243",
    "100.400.260",
    "100.400.300",
    "100.400.301",
    "100.400.302",
    "100.400.303",
    "100.400.304",
    "100.400.305",
    "100.400.306",
    "100.400.307",
    "100.400.308",
    "100.400.309",
    "100.400.310",
    "100.400.311",
    "100.400.312",
    "100.400.313",
    "100.400.314",
    "100.400.315",
    "100.400.316",
    "100.400.317",
    "100.400.318",
    "100.400.319",
    "100.400.320",
    "100.400.321",
    "100.400.322",
    "100.400.323",
    "100.400.324",
    "100.400.325",
    "100.400.326",
    "100.400.327",
    "100.400.328",
    "800.400.100",
    "800.400.101",
    "800.400.102",
    "800.400.103",
    "800.400.104",
    "800.400.105",
    "800.400.110",
    "800.400.150",
    "800.400.151",
    "100.380.401",
    "100.390.101",
    "100.390.102",
    "100.390.103",
    "100.390.104",
    "100.390.105",
    "100.390.106",
    "100.390.107",
    "100.390.108",
    "100.390.109",
    "100.390.110",
    "100.390.111",
    "100.390.112",
    "100.390.113",
    "100.390.114",
    "100.390.115",
    "100.390.116",
    "100.390.117",
    "100.390.118",
    "800.400.200",
    "100.100.701",
    "800.200.159",
    "800.200.160",
    "800.200.165",
    "800.200.202",
    "800.200.208",
    "800.200.220",
    "800.300.101",
    "800.300.102",
    "800.300.200",
    "800.300.301",
    "800.300.302",
    "800.300.401",
    "800.300.500",
    "800.300.501",
    "800.310.200",
    "800.310.210",
    "800.310.211",
    "800.110.100",
    "800.120.100",
    "800.120.101",
    "800.120.102",
    "800.120.103",
    "800.120.200",
    "800.120.201",
    "800.120.202",
    "800.120.203",
    "800.120.300",
    "800.120.401",
    "800.130.100",
    "800.140.100",
    "800.140.101",
    "800.140.110",
    "800.140.111",
    "800.140.112",
    "800.140.113",
    "800.150.100",
    "800.160.100",
    "800.160.110",
    "800.160.120",
    "800.160.130",
    "500.100.201",
    "500.100.202",
    "500.100.203",
    "500.100.301",
    "500.100.302",
    "500.100.303",
    "500.100.304",
    "500.100.401",
    "500.100.402",
    "500.100.403",
    "500.200.101",
    "600.200.100",
    "600.200.200",
    "600.200.201",
    "600.200.202",
    "600.200.300",
    "600.200.310",
    "600.200.400",
    "600.200.500",
    "600.200.501",
    "600.200.600",
    "600.200.700",
    "600.200.800",
    "600.200.810",
    "600.300.101",
    "600.300.200",
    "600.300.210",
    "600.300.211",
    "800.121.100",
    "800.121.200",
    "100.150.100",
    "100.150.101",
    "100.150.200",
    "100.150.201",
    "100.150.202",
    "100.150.203",
    "100.150.204",
    "100.150.205",
    "100.150.300",
    "100.350.100",
    "100.350.101",
    "100.350.200",
    "100.350.201",
    "100.350.301",
    "100.350.302",
    "100.350.303",
    "100.350.310",
    "100.350.311",
    "100.350.312",
    "100.350.313",
    "100.350.314",
    "100.350.315",
    "100.350.316",
    "100.350.400",
    "100.350.500",
    "100.350.601",
    "100.350.602",
    "100.250.100",
    "100.250.105",
    "100.250.106",
    "100.250.107",
    "100.250.110",
    "100.250.111",
    "100.250.120",
    "100.250.121",
    "100.250.122",
    "100.250.123",
    "100.250.124",
    "100.250.125",
    "100.250.250",
    "100.360.201",
    "100.360.300",
    "100.360.303",
    "100.360.400",
    "700.100.100",
    "700.100.200",
    "700.100.300",
    "700.100.400",
    "700.100.500",
    "700.100.600",
    "700.100.700",
    "700.100.701",
    "700.100.710",
    "700.300.100",
    "700.300.200",
    "700.300.300",
    "700.300.400",
    "700.300.500",
    "700.300.600",
    "700.300.700",
    "700.300.800",
    "700.400.000",
    "700.400.100",
    "700.400.101",
    "700.400.200",
    "700.400.300",
    "700.400.400",
    "700.400.402",
    "700.400.410",
    "700.400.411",
    "700.400.420",
    "700.400.510",
    "700.400.520",
    "700.400.530",
    "700.400.540",
    "700.400.550",
    "700.400.560",
    "700.400.561",
    "700.400.562",
    "700.400.565",
    "700.400.570",
    "700.400.580",
    "700.400.590",
    "700.400.600",
    "700.400.700",
    "700.450.001",
    "700.500.001",
    "700.500.002",
    "700.500.003",
    "700.500.004",
    "100.300.101",
    "100.300.200",
    "100.300.300",
    "100.300.400",
    "100.300.401",
    "100.300.402",
    "100.300.501",
    "100.300.600",
    "100.300.601",
    "100.300.700",
    "100.300.701",
    "100.370.100",
    "100.370.101",
    "100.370.102",
    "100.370.110",
    "100.370.111",
    "100.370.121",
    "100.370.122",
    "100.370.123",
    "100.370.124",
    "100.370.125",
    "100.370.131",
    "100.370.132",
    "100.500.101",
    "100.500.201",
    "100.500.301",
    "100.500.302",
    "100.500.303",
    "100.500.304",
    "100.600.500",
    "100.900.500",
    "200.100.101",
    "200.100.102",
    "200.100.103",
    "200.100.150",
    "200.100.151",
    "200.100.199",
    "200.100.201",
    "200.100.300",
    "200.100.301",
    "200.100.302",
    "200.100.401",
    "200.100.402",
    "200.100.403",
    "200.100.404",
    "200.100.501",
    "200.100.502",
    "200.100.503",
    "200.100.504",
    "200.100.601",
    "200.100.602",
    "200.100.603",
    "200.200.106",
    "200.300.403",
    "200.300.404",
    "200.300.405",
    "200.300.406",
    "200.300.407",
    "800.900.100",
    "800.900.101",
    "800.900.200",
    "800.900.201",
    "800.900.300",
    "800.900.301",
    "800.900.302",
    "800.900.303",
    "800.900.399",
    "800.900.401",
    "800.900.450",
    "100.800.100",
    "100.800.101",
    "100.800.102",
    "100.800.200",
    "100.800.201",
    "100.800.202",
    "100.800.300",
    "100.800.301",
    "100.800.302",
    "100.800.400",
    "100.800.401",
    "100.800.500",
    "100.800.501",
    "100.700.100",
    "100.700.101",
    "100.700.200",
    "100.700.201",
    "100.700.300",
    "100.700.400",
    "100.700.500",
    "100.700.800",
    "100.700.801",
    "100.700.802",
    "100.700.810",
    "100.900.100",
    "100.900.101",
    "100.900.105",
    "100.900.200",
    "100.900.300",
    "100.900.301",
    "100.900.400",
    "100.900.401",
    "100.900.450",
    "100.900.500",
    "100.100.100",
    "100.100.101",
    "100.100.104",
    "100.100.200",
    "100.100.201",
    "100.100.300",
    "100.100.301",
    "100.100.303",
    "100.100.304",
    "100.100.305",
    "100.100.400",
    "100.100.401",
    "100.100.402",
    "100.100.500",
    "100.100.501",
    "100.100.600",
    "100.100.601",
    "100.100.650",
    "100.100.651",
    "100.100.700",
    "100.100.701",
    "100.200.100",
    "100.200.103",
    "100.200.104",
    "100.200.200",
    "100.210.101",
    "100.210.102",
    "100.211.101",
    "100.211.102",
    "100.211.103",
    "100.211.104",
    "100.211.105",
    "100.211.106",
    "100.212.101",
    "100.212.102",
    "100.212.103",
    "100.550.300",
    "100.550.301",
    "100.550.303",
    "100.550.310",
    "100.550.311",
    "100.550.312",
    "100.550.400",
    "100.550.401",
    "100.550.601",
    "100.550.603",
    "100.550.605",
    "100.550.701",
    "100.550.702",
    "100.380.101",
    "100.380.201",
    "100.380.305",
    "100.380.306",
    "800.100.100",
    "800.100.150",
    "800.100.151",
    "800.100.152",
    "800.100.153",
    "800.100.154",
    "800.100.155",
    "800.100.156",
    "800.100.157",
    "800.100.158",
    "800.100.159",
    "800.100.160",
    "800.100.161",
    "800.100.162",
    "800.100.163",
    "800.100.164",
    "800.100.165",
    "800.100.166",
    "800.100.167",
    "800.100.168",
    "800.100.169",
    "800.100.170",
    "800.100.171",
    "800.100.172",
    "800.100.173",
    "800.100.174",
    "800.100.175",
    "800.100.176",
    "800.100.177",
    "800.100.178",
    "800.100.179",
    "800.100.190",
    "800.100.191",
    "800.100.192",
    "800.100.195",
    "800.100.196",
    "800.100.197",
    "800.100.198",
    "800.100.199",
    "800.100.200",
    "800.100.201",
    "800.100.202",
    "800.100.203",
    "800.100.204",
    "800.100.205",
    "800.100.206",
    "800.100.207",
    "800.100.208",
    "800.100.402",
    "800.100.403",
    "800.100.500",
    "800.100.501",
    "800.700.100",
    "800.700.101",
    "800.700.201",
    "800.700.500",
    "800.800.102",
    "800.800.202",
    "800.800.302",
];

pub(super) const SUCCESSFUL_CODES: [&str; 16] = [
    "000.000.000",
    "000.000.100",
    "000.100.105",
    "000.100.106",
    "000.100.110",
    "000.100.111",
    "000.100.112",
    "000.300.000",
    "000.300.100",
    "000.300.101",
    "000.300.102",
    "000.300.103",
    "000.310.100",
    "000.310.101",
    "000.310.110",
    "000.600.000",
];

pub(super) const PENDING_CODES: [&str; 26] = [
    "000.400.000",
    "000.400.010",
    "000.400.020",
    "000.400.040",
    "000.400.050",
    "000.400.060",
    "000.400.070",
    "000.400.080",
    "000.400.081",
    "000.400.082",
    "000.400.090",
    "000.400.091",
    "000.400.100",
    "000.400.110",
    "000.200.000",
    "000.200.001",
    "000.200.100",
    "000.200.101",
    "000.200.102",
    "000.200.103",
    "000.200.200",
    "000.200.201",
    "100.400.500",
    "800.400.500",
    "800.400.501",
    "800.400.502",
];


File: crates/router/src/core/payments/operations/payment_status.rs
 Public functions: 1
 Public structs: 1

use std::marker::PhantomData;

use api_models::enums::FrmSuggestion;
use async_trait::async_trait;
use common_utils::{ext_traits::AsyncExt, types::keymanager::KeyManagerState};
use error_stack::ResultExt;
use router_derive::PaymentOperation;
use router_env::{instrument, logger, tracing};

use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
use crate::{
    core::{
        errors::{self, CustomResult, RouterResult, StorageErrorExt},
        payments::{
            helpers, operations, types as payment_types, CustomerDetails, PaymentAddress,
            PaymentData,
        },
    },
    events::audit_events::{AuditEvent, AuditEventType},
    routes::{app::ReqState, SessionState},
    services,
    types::{
        api, domain,
        storage::{self, enums},
    },
    utils::OptionExt,
};

#[derive(Debug, Clone, Copy, PaymentOperation)]
#[operation(operations = "all", flow = "sync")]
pub struct PaymentStatus;

type PaymentStatusOperation<'b, F, R> = BoxedOperation<'b, F, R, PaymentData<F>>;

impl<F: Send + Clone + Sync> Operation<F, api::PaymentsRequest> for PaymentStatus {
    type Data = PaymentData<F>;
    fn to_domain(&self) -> RouterResult<&dyn Domain<F, api::PaymentsRequest, PaymentData<F>>> {
        Ok(self)
    }
    fn to_update_tracker(
        &self,
    ) -> RouterResult<&(dyn UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> + Send + Sync)>
    {
        Ok(self)
    }
}
impl<F: Send + Clone + Sync> Operation<F, api::PaymentsRequest> for &PaymentStatus {
    type Data = PaymentData<F>;
    fn to_domain(&self) -> RouterResult<&dyn Domain<F, api::PaymentsRequest, PaymentData<F>>> {
        Ok(*self)
    }
    fn to_update_tracker(
        &self,
    ) -> RouterResult<&(dyn UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> + Send + Sync)>
    {
        Ok(*self)
    }
}

#[async_trait]
impl<F: Clone + Send + Sync> Domain<F, api::PaymentsRequest, PaymentData<F>> for PaymentStatus {
    #[instrument(skip_all)]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut PaymentData<F>,
        request: Option<CustomerDetails>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            PaymentStatusOperation<'a, F, api::PaymentsRequest>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        helpers::create_customer_if_not_exist(
            state,
            Box::new(self),
            payment_data,
            request,
            &key_store.merchant_id,
            key_store,
            storage_scheme,
        )
        .await
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut PaymentData<F>,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        PaymentStatusOperation<'a, F, api::PaymentsRequest>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    #[instrument(skip_all)]
    async fn add_task_to_process_tracker<'a>(
        &'a self,
        state: &'a SessionState,
        payment_attempt: &storage::PaymentAttempt,
        requeue: bool,
        schedule_time: Option<time::PrimitiveDateTime>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        helpers::add_domain_task_to_pt(self, state, payment_attempt, requeue, schedule_time).await
    }

    async fn get_connector<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        state: &SessionState,
        request: &api::PaymentsRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, request.routing.clone()).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut PaymentData<F>,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[async_trait]
impl<F: Clone + Sync> UpdateTracker<F, PaymentData<F>, api::PaymentsRequest> for PaymentStatus {
    async fn update_trackers<'b>(
        &'b self,
        _state: &'b SessionState,
        req_state: ReqState,
        payment_data: PaymentData<F>,
        _customer: Option<domain::Customer>,
        _storage_scheme: enums::MerchantStorageScheme,
        _updated_customer: Option<storage::CustomerUpdate>,
        _key_store: &domain::MerchantKeyStore,
        _frm_suggestion: Option<FrmSuggestion>,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(
        PaymentStatusOperation<'b, F, api::PaymentsRequest>,
        PaymentData<F>,
    )>
    where
        F: 'b + Send,
    {
        req_state
            .event_context
            .event(AuditEvent::new(AuditEventType::PaymentStatus))
            .with(payment_data.to_event())
            .emit();

        Ok((Box::new(self), payment_data))
    }
}

#[async_trait]
impl<F: Clone + Sync> UpdateTracker<F, PaymentData<F>, api::PaymentsRetrieveRequest>
    for PaymentStatus
{
    async fn update_trackers<'b>(
        &'b self,
        _state: &'b SessionState,
        req_state: ReqState,
        payment_data: PaymentData<F>,
        _customer: Option<domain::Customer>,
        _storage_scheme: enums::MerchantStorageScheme,
        _updated_customer: Option<storage::CustomerUpdate>,
        _key_store: &domain::MerchantKeyStore,
        _frm_suggestion: Option<FrmSuggestion>,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(
        PaymentStatusOperation<'b, F, api::PaymentsRetrieveRequest>,
        PaymentData<F>,
    )>
    where
        F: 'b + Send,
    {
        req_state
            .event_context
            .event(AuditEvent::new(AuditEventType::PaymentStatus))
            .with(payment_data.to_event())
            .emit();

        Ok((Box::new(self), payment_data))
    }
}

#[async_trait]
impl<F: Send + Clone + Sync> GetTracker<F, PaymentData<F>, api::PaymentsRetrieveRequest>
    for PaymentStatus
{
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &api::PaymentIdType,
        request: &api::PaymentsRetrieveRequest,
        merchant_context: &domain::MerchantContext,
        _auth_flow: services::AuthFlow,
        _header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<
        operations::GetTrackerResponse<'a, F, api::PaymentsRetrieveRequest, PaymentData<F>>,
    > {
        Box::pin(get_tracker_for_sync(
            payment_id,
            &merchant_context.clone(),
            state,
            request,
            self,
            merchant_context.get_merchant_account().storage_scheme,
        ))
        .await
    }
}

#[cfg(feature = "v2")]
async fn get_tracker_for_sync<
    'a,
    F: Send + Clone,
    Op: Operation<F, api::PaymentsRetrieveRequest, Data = PaymentData<F>> + 'a + Send + Sync,
>(
    _payment_id: &api::PaymentIdType,
    _merchant_context: &domain::MerchantContext,
    _state: &SessionState,
    _request: &api::PaymentsRetrieveRequest,
    _operation: Op,
    _storage_scheme: enums::MerchantStorageScheme,
) -> RouterResult<operations::GetTrackerResponse<'a, F, api::PaymentsRetrieveRequest, PaymentData<F>>>
{
    todo!()
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
async fn get_tracker_for_sync<
    'a,
    F: Send + Clone,
    Op: Operation<F, api::PaymentsRetrieveRequest, Data = PaymentData<F>> + 'a + Send + Sync,
>(
    payment_id: &api::PaymentIdType,
    merchant_context: &domain::MerchantContext,
    state: &SessionState,
    request: &api::PaymentsRetrieveRequest,
    operation: Op,
    storage_scheme: enums::MerchantStorageScheme,
) -> RouterResult<operations::GetTrackerResponse<'a, F, api::PaymentsRetrieveRequest, PaymentData<F>>>
{
    let (payment_intent, mut payment_attempt, currency, amount);

    (payment_intent, payment_attempt) = get_payment_intent_payment_attempt(
        state,
        payment_id,
        merchant_context.get_merchant_account().get_id(),
        merchant_context.get_merchant_key_store(),
        storage_scheme,
    )
    .await?;

    helpers::authenticate_client_secret(request.client_secret.as_ref(), &payment_intent)?;

    let payment_id = payment_attempt.payment_id.clone();

    currency = payment_attempt.currency.get_required_value("currency")?;
    amount = payment_attempt.get_total_amount().into();

    let shipping_address = helpers::get_address_by_id(
        state,
        payment_intent.shipping_address_id.clone(),
        merchant_context.get_merchant_key_store(),
        &payment_intent.payment_id.clone(),
        merchant_context.get_merchant_account().get_id(),
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await?;
    let billing_address = helpers::get_address_by_id(
        state,
        payment_intent.billing_address_id.clone(),
        merchant_context.get_merchant_key_store(),
        &payment_intent.payment_id.clone(),
        merchant_context.get_merchant_account().get_id(),
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await?;

    let payment_method_billing = helpers::get_address_by_id(
        state,
        payment_attempt.payment_method_billing_address_id.clone(),
        merchant_context.get_merchant_key_store(),
        &payment_intent.payment_id.clone(),
        merchant_context.get_merchant_account().get_id(),
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await?;

    payment_attempt.encoded_data.clone_from(&request.param);
    let db = &*state.store;
    let key_manager_state = &state.into();
    let attempts = match request.expand_attempts {
        Some(true) => {
            Some(db
                .find_attempts_by_merchant_id_payment_id(merchant_context.get_merchant_account().get_id(), &payment_id, storage_scheme)
                .await
                .change_context(errors::ApiErrorResponse::PaymentNotFound)
                .attach_printable_lazy(|| {
                    format!("Error while retrieving attempt list for, merchant_id: {:?}, payment_id: {payment_id:?}",merchant_context.get_merchant_account().get_id())
                })?)
        },
        _ => None,
    };

    let multiple_capture_data = if payment_attempt.multiple_capture_count > Some(0) {
        let captures = db
            .find_all_captures_by_merchant_id_payment_id_authorized_attempt_id(
                &payment_attempt.merchant_id,
                &payment_attempt.payment_id,
                &payment_attempt.attempt_id,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::PaymentNotFound)
                .attach_printable_lazy(|| {
                    format!("Error while retrieving capture list for, merchant_id: {:?}, payment_id: {payment_id:?}", merchant_context.get_merchant_account().get_id())
                })?;
        Some(payment_types::MultipleCaptureData::new_for_sync(
            captures,
            request.expand_captures,
        )?)
    } else {
        None
    };

    let refunds = db
        .find_refund_by_payment_id_merchant_id(
            &payment_id,
            merchant_context.get_merchant_account().get_id(),
            storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable_lazy(|| {
            format!(
                "Failed while getting refund list for, payment_id: {:?}, merchant_id: {:?}",
                &payment_id,
                merchant_context.get_merchant_account().get_id()
            )
        })?;

    let authorizations = db
        .find_all_authorizations_by_merchant_id_payment_id(
            merchant_context.get_merchant_account().get_id(),
            &payment_id,
        )
        .await
        .change_context(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable_lazy(|| {
            format!(
                "Failed while getting authorizations list for, payment_id: {:?}, merchant_id: {:?}",
                &payment_id,
                merchant_context.get_merchant_account().get_id()
            )
        })?;

    let disputes = db
        .find_disputes_by_merchant_id_payment_id(merchant_context.get_merchant_account().get_id(), &payment_id)
        .await
        .change_context(errors::ApiErrorResponse::PaymentNotFound)
        .attach_printable_lazy(|| {
            format!("Error while retrieving dispute list for, merchant_id: {:?}, payment_id: {payment_id:?}", merchant_context.get_merchant_account().get_id())
        })?;

    let frm_response = if cfg!(feature = "frm") {
        db.find_fraud_check_by_payment_id(payment_id.to_owned(), merchant_context.get_merchant_account().get_id().clone())
            .await
            .change_context(errors::ApiErrorResponse::PaymentNotFound)
            .attach_printable_lazy(|| {
                format!("Error while retrieving frm_response, merchant_id: {:?}, payment_id: {payment_id:?}", merchant_context.get_merchant_account().get_id())
            })
            .ok()
    } else {
        None
    };

    let contains_encoded_data = payment_attempt.encoded_data.is_some();

    let creds_identifier = request
        .merchant_connector_details
        .as_ref()
        .map(|mcd| mcd.creds_identifier.to_owned());
    request
        .merchant_connector_details
        .to_owned()
        .async_map(|mcd| async {
            helpers::insert_merchant_connector_creds_to_config(
                db,
                merchant_context.get_merchant_account().get_id(),
                mcd,
            )
            .await
        })
        .await
        .transpose()?;

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

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

    let payment_method_info =
        if let Some(ref payment_method_id) = payment_attempt.payment_method_id.clone() {
            match db
                .find_payment_method(
                    &(state.into()),
                    merchant_context.get_merchant_key_store(),
                    payment_method_id,
                    storage_scheme,
                )
                .await
            {
                Ok(payment_method) => Some(payment_method),
                Err(error) => {
                    if error.current_context().is_db_not_found() {
                        logger::info!("Payment Method not found in db {:?}", error);
                        None
                    } else {
                        Err(error)
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Error retrieving payment method from db")?
                    }
                }
            }
        } else {
            None
        };

    let merchant_id = payment_intent.merchant_id.clone();

    let authentication_store = if let Some(ref authentication_id) =
        payment_attempt.authentication_id
    {
        let authentication = db
            .find_authentication_by_merchant_id_authentication_id(&merchant_id, authentication_id)
            .await
            .to_not_found_response(errors::ApiErrorResponse::InternalServerError)
            .attach_printable_lazy(|| {
                format!(
                    "Error while fetching authentication record with authentication_id {}",
                    authentication_id.get_string_repr()
                )
            })?;

        Some(
            hyperswitch_domain_models::router_request_types::authentication::AuthenticationStore {
                authentication,
                cavv: None, // marking this as None since we don't need authentication value in payment status flow
            },
        )
    } else {
        None
    };

    let payment_link_data = payment_intent
        .payment_link_id
        .as_ref()
        .async_map(|id| crate::core::payments::get_payment_link_response_from_id(state, id))
        .await
        .transpose()?;

    let payment_data = PaymentData {
        flow: PhantomData,
        payment_intent,
        currency,
        amount,
        email: None,
        mandate_id: payment_attempt
            .mandate_id
            .clone()
            .map(|id| api_models::payments::MandateIds {
                mandate_id: Some(id),
                mandate_reference_id: None,
            }),
        mandate_connector: None,
        setup_mandate: None,
        customer_acceptance: None,
        token: None,
        address: PaymentAddress::new(
            shipping_address.as_ref().map(From::from),
            billing_address.as_ref().map(From::from),
            payment_method_billing.as_ref().map(From::from),
            business_profile.use_billing_as_payment_method_billing,
        ),
        token_data: None,
        confirm: Some(request.force_sync),
        payment_method_data: None,
        payment_method_token: None,
        payment_method_info,
        force_sync: Some(
            request.force_sync
                && (helpers::check_force_psync_precondition(payment_attempt.status)
                    || contains_encoded_data),
        ),
        all_keys_required: request.all_keys_required,
        payment_attempt,
        refunds,
        disputes,
        attempts,
        sessions_token: vec![],
        card_cvc: None,
        creds_identifier,
        pm_token: None,
        connector_customer_id: None,
        recurring_mandate_payment_data: None,
        ephemeral_key: None,
        multiple_capture_data,
        redirect_response: None,
        payment_link_data,
        surcharge_details: None,
        frm_message: frm_response,
        incremental_authorization_details: None,
        authorizations,
        authentication: authentication_store,
        recurring_details: None,
        poll_config: None,
        tax_data: None,
        session_id: None,
        service_details: None,
        card_testing_guard_data: None,
        vault_operation: None,
        threeds_method_comp_ind: None,
        whole_connector_response: None,
    };

    let get_trackers_response = operations::GetTrackerResponse {
        operation: Box::new(operation),
        customer_details: None,
        payment_data,
        business_profile,
        mandate_type: None,
    };

    Ok(get_trackers_response)
}

impl<F: Send + Clone + Sync> ValidateRequest<F, api::PaymentsRetrieveRequest, PaymentData<F>>
    for PaymentStatus
{
    fn validate_request<'b>(
        &'b self,
        request: &api::PaymentsRetrieveRequest,
        merchant_context: &domain::MerchantContext,
    ) -> RouterResult<(
        PaymentStatusOperation<'b, F, api::PaymentsRetrieveRequest>,
        operations::ValidateResult,
    )> {
        let request_merchant_id = request.merchant_id.as_ref();
        helpers::validate_merchant_id(
            merchant_context.get_merchant_account().get_id(),
            request_merchant_id,
        )
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "merchant_id".to_string(),
            expected_format: "merchant_id from merchant account".to_string(),
        })?;

        Ok((
            Box::new(self),
            operations::ValidateResult {
                merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
                payment_id: request.resource_id.clone(),
                storage_scheme: merchant_context.get_merchant_account().storage_scheme,
                requeue: false,
            },
        ))
    }
}

pub async fn get_payment_intent_payment_attempt(
    state: &SessionState,
    payment_id: &api::PaymentIdType,
    merchant_id: &common_utils::id_type::MerchantId,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: enums::MerchantStorageScheme,
) -> RouterResult<(storage::PaymentIntent, storage::PaymentAttempt)> {
    let key_manager_state: KeyManagerState = state.into();
    let db = &*state.store;
    let get_pi_pa = || async {
        let (pi, pa);
        match payment_id {
            api_models::payments::PaymentIdType::PaymentIntentId(ref id) => {
                pi = db
                    .find_payment_intent_by_payment_id_merchant_id(
                        &key_manager_state,
                        id,
                        merchant_id,
                        key_store,
                        storage_scheme,
                    )
                    .await?;
                pa = db
                    .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                        &pi.payment_id,
                        merchant_id,
                        pi.active_attempt.get_id().as_str(),
                        storage_scheme,
                    )
                    .await?;
            }
            api_models::payments::PaymentIdType::ConnectorTransactionId(ref id) => {
                pa = db
                    .find_payment_attempt_by_merchant_id_connector_txn_id(
                        merchant_id,
                        id,
                        storage_scheme,
                    )
                    .await?;
                pi = db
                    .find_payment_intent_by_payment_id_merchant_id(
                        &key_manager_state,
                        &pa.payment_id,
                        merchant_id,
                        key_store,
                        storage_scheme,
                    )
                    .await?;
            }
            api_models::payments::PaymentIdType::PaymentAttemptId(ref id) => {
                pa = db
                    .find_payment_attempt_by_attempt_id_merchant_id(id, merchant_id, storage_scheme)
                    .await?;
                pi = db
                    .find_payment_intent_by_payment_id_merchant_id(
                        &key_manager_state,
                        &pa.payment_id,
                        merchant_id,
                        key_store,
                        storage_scheme,
                    )
                    .await?;
            }
            api_models::payments::PaymentIdType::PreprocessingId(ref id) => {
                pa = db
                    .find_payment_attempt_by_preprocessing_id_merchant_id(
                        id,
                        merchant_id,
                        storage_scheme,
                    )
                    .await?;

                pi = db
                    .find_payment_intent_by_payment_id_merchant_id(
                        &key_manager_state,
                        &pa.payment_id,
                        merchant_id,
                        key_store,
                        storage_scheme,
                    )
                    .await?;
            }
        }
        error_stack::Result::<_, errors::StorageError>::Ok((pi, pa))
    };

    get_pi_pa()
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)

    // TODO (#7195): Add platform merchant account validation once client_secret auth is solved
}


impl api::Refund for Juspaythreedsserver {}

impl api::Refund for for Juspaythreedsserver


impl api::ExternalVaultCreate for HyperswitchVault {}

impl api::ExternalVaultCreate for for HyperswitchVault


pub fn get_payout_attempt_id(payout_id: &str, attempt_count: i16) -> String {
    format!("{payout_id}_{attempt_count}")
}


pub struct KlarnaCheckoutRequestData {
    merchant_urls: MerchantURLs,
    options: CheckoutOptions,
}


pub fn get_id(&self) -> &id_type::GlobalCustomerId {
        &self.id
    }


pub async fn get_theme_using_theme_id(
    state: SessionState,
    theme_id: String,
) -> UserResponse<theme_api::GetThemeResponse> {
    let theme = state
        .store
        .find_theme_by_theme_id(theme_id.clone())
        .await
        .to_not_found_response(UserErrors::ThemeNotFound)?;

    let file = theme_utils::retrieve_file_from_theme_bucket(
        &state,
        &theme_utils::get_theme_file_key(&theme_id),
    )
    .await?;

    let parsed_data = file
        .to_bytes()
        .parse_struct("ThemeData")
        .change_context(UserErrors::InternalServerError)?;

    Ok(ApplicationResponse::Json(theme_api::GetThemeResponse {
        email_config: theme.email_config(),
        theme_id: theme.theme_id,
        theme_name: theme.theme_name,
        entity_type: theme.entity_type,
        tenant_id: theme.tenant_id,
        org_id: theme.org_id,
        merchant_id: theme.merchant_id,
        profile_id: theme.profile_id,
        theme_data: parsed_data,
    }))
}


pub struct PaymentsErrorResponse {
    pub rc: String,
    pub message: String,
}


OpenAPI Block Path: components.schemas.CustomerResponse
{
  "type": "object",
  "required": [
    "customer_id",
    "created_at"
  ],
  "properties": {
    "customer_id": {
      "type": "string",
      "description": "The identifier for the customer object",
      "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44",
      "maxLength": 64,
      "minLength": 1
    },
    "name": {
      "type": "string",
      "description": "The customer's name",
      "example": "Jon Test",
      "nullable": true,
      "maxLength": 255
    },
    "email": {
      "type": "string",
      "description": "The customer's email address",
      "example": "JonTest@test.com",
      "nullable": true,
      "maxLength": 255
    },
    "phone": {
      "type": "string",
      "description": "The customer's phone number",
      "example": "9123456789",
      "nullable": true,
      "maxLength": 255
    },
    "phone_country_code": {
      "type": "string",
      "description": "The country code for the customer phone number",
      "example": "+65",
      "nullable": true,
      "maxLength": 255
    },
    "description": {
      "type": "string",
      "description": "An arbitrary string that you can attach to a customer object.",
      "example": "First Customer",
      "nullable": true,
      "maxLength": 255
    },
    "address": {
      "allOf": [
        {
          "$ref": "#/components/schemas/AddressDetails"
        }
      ],
      "nullable": true
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "description": "A timestamp (ISO 8601 code) that determines when the customer was created",
      "example": "2023-01-18T11:04:09.922Z"
    },
    "metadata": {
      "type": "object",
      "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500\ncharacters long. Metadata is useful for storing additional, structured information on an\nobject.",
      "nullable": true
    },
    "default_payment_method_id": {
      "type": "string",
      "description": "The identifier for the default payment method.",
      "example": "pm_djh2837dwduh890123",
      "nullable": true,
      "maxLength": 64
    },
    "tax_registration_id": {
      "type": "string",
      "description": "The customer's tax registration number.",
      "example": "123456789",
      "nullable": true,
      "maxLength": 255
    }
  }
}


pub trait ConnectorCommon {
    fn id(&self) -> &'static str;

    fn get_auth_header(
        &self,
        _auth_type: &auth_types::ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        Ok(Vec::new())
    }

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

    fn base_url<'a>(&self, connectors: &'a auth_types::PaymentMethodAuthConnectors) -> &'a str;

    fn build_error_response(
        &self,
        res: auth_types::Response,
    ) -> CustomResult<auth_types::ErrorResponse, ConnectorError> {
        Ok(auth_types::ErrorResponse {
            status_code: res.status_code,
            code: crate::consts::NO_ERROR_CODE.to_string(),
            message: crate::consts::NO_ERROR_MESSAGE.to_string(),
            reason: None,
        })
    }
}


pub async fn refunds_metadata_update() {}