File: crates/diesel_models/src/refund.rs
 Public functions: 8
 Public structs: 8

use common_utils::{
    id_type, pii,
    types::{ChargeRefunds, ConnectorTransactionId, ConnectorTransactionIdTrait, MinorUnit},
};
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::enums as storage_enums;
#[cfg(feature = "v1")]
use crate::schema::refund;
#[cfg(feature = "v2")]
use crate::{schema_v2::refund, RequiredFromNullable};

#[cfg(feature = "v1")]
#[derive(
    Clone,
    Debug,
    Eq,
    Identifiable,
    Queryable,
    Selectable,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
)]
#[diesel(table_name = refund, primary_key(refund_id), check_for_backend(diesel::pg::Pg))]
pub struct Refund {
    pub internal_reference_id: String,
    pub refund_id: String, //merchant_reference id
    pub payment_id: id_type::PaymentId,
    pub merchant_id: id_type::MerchantId,
    pub connector_transaction_id: ConnectorTransactionId,
    pub connector: String,
    pub connector_refund_id: Option<ConnectorTransactionId>,
    pub external_reference_id: Option<String>,
    pub refund_type: storage_enums::RefundType,
    pub total_amount: MinorUnit,
    pub currency: storage_enums::Currency,
    pub refund_amount: MinorUnit,
    pub refund_status: storage_enums::RefundStatus,
    pub sent_to_gateway: bool,
    pub refund_error_message: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub refund_arn: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    pub description: Option<String>,
    pub attempt_id: String,
    pub refund_reason: Option<String>,
    pub refund_error_code: Option<String>,
    pub profile_id: Option<id_type::ProfileId>,
    pub updated_by: String,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub charges: Option<ChargeRefunds>,
    pub organization_id: id_type::OrganizationId,
    /// INFO: This field is deprecated and replaced by processor_refund_data
    pub connector_refund_data: Option<String>,
    /// INFO: This field is deprecated and replaced by processor_transaction_data
    pub connector_transaction_data: Option<String>,
    pub split_refunds: Option<common_types::refunds::SplitRefund>,
    pub unified_code: Option<String>,
    pub unified_message: Option<String>,
    pub processor_refund_data: Option<String>,
    pub processor_transaction_data: Option<String>,
    pub issuer_error_code: Option<String>,
    pub issuer_error_message: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(
    Clone,
    Debug,
    Eq,
    Identifiable,
    Queryable,
    Selectable,
    PartialEq,
    serde::Serialize,
    serde::Deserialize,
)]
#[diesel(table_name = refund, primary_key(id), check_for_backend(diesel::pg::Pg))]
pub struct Refund {
    pub payment_id: id_type::GlobalPaymentId,
    pub merchant_id: id_type::MerchantId,
    pub connector_transaction_id: ConnectorTransactionId,
    pub connector: String,
    pub connector_refund_id: Option<ConnectorTransactionId>,
    pub external_reference_id: Option<String>,
    pub refund_type: storage_enums::RefundType,
    pub total_amount: MinorUnit,
    pub currency: storage_enums::Currency,
    pub refund_amount: MinorUnit,
    pub refund_status: storage_enums::RefundStatus,
    pub sent_to_gateway: bool,
    pub refund_error_message: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub refund_arn: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    pub description: Option<String>,
    pub attempt_id: id_type::GlobalAttemptId,
    pub refund_reason: Option<String>,
    pub refund_error_code: Option<String>,
    pub profile_id: Option<id_type::ProfileId>,
    pub updated_by: String,
    pub charges: Option<ChargeRefunds>,
    pub organization_id: id_type::OrganizationId,
    pub split_refunds: Option<common_types::refunds::SplitRefund>,
    pub unified_code: Option<String>,
    pub unified_message: Option<String>,
    pub processor_refund_data: Option<String>,
    pub processor_transaction_data: Option<String>,
    pub id: id_type::GlobalRefundId,
    #[diesel(deserialize_as = RequiredFromNullable<id_type::RefundReferenceId>)]
    pub merchant_reference_id: id_type::RefundReferenceId,
    pub connector_id: Option<id_type::MerchantConnectorAccountId>,
}

#[cfg(feature = "v1")]
#[derive(
    Clone,
    Debug,
    Eq,
    PartialEq,
    Insertable,
    router_derive::DebugAsDisplay,
    serde::Serialize,
    serde::Deserialize,
    router_derive::Setter,
)]
#[diesel(table_name = refund)]
pub struct RefundNew {
    pub refund_id: String,
    pub payment_id: id_type::PaymentId,
    pub merchant_id: id_type::MerchantId,
    pub internal_reference_id: String,
    pub external_reference_id: Option<String>,
    pub connector_transaction_id: ConnectorTransactionId,
    pub connector: String,
    pub connector_refund_id: Option<ConnectorTransactionId>,
    pub refund_type: storage_enums::RefundType,
    pub total_amount: MinorUnit,
    pub currency: storage_enums::Currency,
    pub refund_amount: MinorUnit,
    pub refund_status: storage_enums::RefundStatus,
    pub sent_to_gateway: bool,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub refund_arn: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    pub description: Option<String>,
    pub attempt_id: String,
    pub refund_reason: Option<String>,
    pub profile_id: Option<id_type::ProfileId>,
    pub updated_by: String,
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub charges: Option<ChargeRefunds>,
    pub organization_id: id_type::OrganizationId,
    pub split_refunds: Option<common_types::refunds::SplitRefund>,
    pub processor_refund_data: Option<String>,
    pub processor_transaction_data: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(
    Clone,
    Debug,
    Eq,
    PartialEq,
    Insertable,
    router_derive::DebugAsDisplay,
    serde::Serialize,
    serde::Deserialize,
    router_derive::Setter,
)]
#[diesel(table_name = refund)]
pub struct RefundNew {
    pub merchant_reference_id: id_type::RefundReferenceId,
    pub payment_id: id_type::GlobalPaymentId,
    pub merchant_id: id_type::MerchantId,
    pub id: id_type::GlobalRefundId,
    pub external_reference_id: Option<String>,
    pub connector_transaction_id: ConnectorTransactionId,
    pub connector: String,
    pub connector_refund_id: Option<ConnectorTransactionId>,
    pub refund_type: storage_enums::RefundType,
    pub total_amount: MinorUnit,
    pub currency: storage_enums::Currency,
    pub refund_amount: MinorUnit,
    pub refund_status: storage_enums::RefundStatus,
    pub sent_to_gateway: bool,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub refund_arn: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    pub description: Option<String>,
    pub attempt_id: id_type::GlobalAttemptId,
    pub refund_reason: Option<String>,
    pub profile_id: Option<id_type::ProfileId>,
    pub updated_by: String,
    pub connector_id: Option<id_type::MerchantConnectorAccountId>,
    pub charges: Option<ChargeRefunds>,
    pub organization_id: id_type::OrganizationId,
    pub split_refunds: Option<common_types::refunds::SplitRefund>,
    pub processor_refund_data: Option<String>,
    pub processor_transaction_data: Option<String>,
}

#[cfg(feature = "v1")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum RefundUpdate {
    Update {
        connector_refund_id: ConnectorTransactionId,
        refund_status: storage_enums::RefundStatus,
        sent_to_gateway: bool,
        refund_error_message: Option<String>,
        refund_arn: String,
        updated_by: String,
        processor_refund_data: Option<String>,
    },
    MetadataAndReasonUpdate {
        metadata: Option<pii::SecretSerdeValue>,
        reason: Option<String>,
        updated_by: String,
    },
    StatusUpdate {
        connector_refund_id: Option<ConnectorTransactionId>,
        sent_to_gateway: bool,
        refund_status: storage_enums::RefundStatus,
        updated_by: String,
        processor_refund_data: Option<String>,
    },
    ErrorUpdate {
        refund_status: Option<storage_enums::RefundStatus>,
        refund_error_message: Option<String>,
        refund_error_code: Option<String>,
        updated_by: String,
        connector_refund_id: Option<ConnectorTransactionId>,
        processor_refund_data: Option<String>,
        unified_code: Option<String>,
        unified_message: Option<String>,
        issuer_error_code: Option<String>,
        issuer_error_message: Option<String>,
    },
    ManualUpdate {
        refund_status: Option<storage_enums::RefundStatus>,
        refund_error_message: Option<String>,
        refund_error_code: Option<String>,
        updated_by: String,
    },
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum RefundUpdate {
    Update {
        connector_refund_id: ConnectorTransactionId,
        refund_status: storage_enums::RefundStatus,
        sent_to_gateway: bool,
        refund_error_message: Option<String>,
        refund_arn: String,
        updated_by: String,
        processor_refund_data: Option<String>,
    },
    MetadataAndReasonUpdate {
        metadata: Option<pii::SecretSerdeValue>,
        reason: Option<String>,
        updated_by: String,
    },
    StatusUpdate {
        connector_refund_id: Option<ConnectorTransactionId>,
        sent_to_gateway: bool,
        refund_status: storage_enums::RefundStatus,
        updated_by: String,
        processor_refund_data: Option<String>,
    },
    ErrorUpdate {
        refund_status: Option<storage_enums::RefundStatus>,
        refund_error_message: Option<String>,
        refund_error_code: Option<String>,
        updated_by: String,
        connector_refund_id: Option<ConnectorTransactionId>,
        processor_refund_data: Option<String>,
        unified_code: Option<String>,
        unified_message: Option<String>,
    },
    ManualUpdate {
        refund_status: Option<storage_enums::RefundStatus>,
        refund_error_message: Option<String>,
        refund_error_code: Option<String>,
        updated_by: String,
    },
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = refund)]
pub struct RefundUpdateInternal {
    connector_refund_id: Option<ConnectorTransactionId>,
    refund_status: Option<storage_enums::RefundStatus>,
    sent_to_gateway: Option<bool>,
    refund_error_message: Option<String>,
    refund_arn: Option<String>,
    metadata: Option<pii::SecretSerdeValue>,
    refund_reason: Option<String>,
    refund_error_code: Option<String>,
    updated_by: String,
    modified_at: PrimitiveDateTime,
    processor_refund_data: Option<String>,
    unified_code: Option<String>,
    unified_message: Option<String>,
    issuer_error_code: Option<String>,
    issuer_error_message: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = refund)]
pub struct RefundUpdateInternal {
    connector_refund_id: Option<ConnectorTransactionId>,
    refund_status: Option<storage_enums::RefundStatus>,
    sent_to_gateway: Option<bool>,
    refund_error_message: Option<String>,
    refund_arn: Option<String>,
    metadata: Option<pii::SecretSerdeValue>,
    refund_reason: Option<String>,
    refund_error_code: Option<String>,
    updated_by: String,
    modified_at: PrimitiveDateTime,
    processor_refund_data: Option<String>,
    unified_code: Option<String>,
    unified_message: Option<String>,
}

#[cfg(feature = "v1")]
impl RefundUpdateInternal {
    pub fn create_refund(self, source: Refund) -> Refund {
        Refund {
            connector_refund_id: self.connector_refund_id,
            refund_status: self.refund_status.unwrap_or_default(),
            sent_to_gateway: self.sent_to_gateway.unwrap_or_default(),
            refund_error_message: self.refund_error_message,
            refund_arn: self.refund_arn,
            metadata: self.metadata,
            refund_reason: self.refund_reason,
            refund_error_code: self.refund_error_code,
            updated_by: self.updated_by,
            modified_at: self.modified_at,
            processor_refund_data: self.processor_refund_data,
            unified_code: self.unified_code,
            unified_message: self.unified_message,
            ..source
        }
    }
}

#[cfg(feature = "v2")]
impl RefundUpdateInternal {
    pub fn create_refund(self, source: Refund) -> Refund {
        Refund {
            connector_refund_id: self.connector_refund_id,
            refund_status: self.refund_status.unwrap_or_default(),
            sent_to_gateway: self.sent_to_gateway.unwrap_or_default(),
            refund_error_message: self.refund_error_message,
            refund_arn: self.refund_arn,
            metadata: self.metadata,
            refund_reason: self.refund_reason,
            refund_error_code: self.refund_error_code,
            updated_by: self.updated_by,
            modified_at: self.modified_at,
            processor_refund_data: self.processor_refund_data,
            unified_code: self.unified_code,
            unified_message: self.unified_message,
            ..source
        }
    }
}

#[cfg(feature = "v1")]
impl From<RefundUpdate> for RefundUpdateInternal {
    fn from(refund_update: RefundUpdate) -> Self {
        match refund_update {
            RefundUpdate::Update {
                connector_refund_id,
                refund_status,
                sent_to_gateway,
                refund_error_message,
                refund_arn,
                updated_by,
                processor_refund_data,
            } => Self {
                connector_refund_id: Some(connector_refund_id),
                refund_status: Some(refund_status),
                sent_to_gateway: Some(sent_to_gateway),
                refund_error_message,
                refund_arn: Some(refund_arn),
                updated_by,
                processor_refund_data,
                metadata: None,
                refund_reason: None,
                refund_error_code: None,
                modified_at: common_utils::date_time::now(),
                unified_code: None,
                unified_message: None,
                issuer_error_code: None,
                issuer_error_message: None,
            },
            RefundUpdate::MetadataAndReasonUpdate {
                metadata,
                reason,
                updated_by,
            } => Self {
                metadata,
                refund_reason: reason,
                updated_by,
                connector_refund_id: None,
                refund_status: None,
                sent_to_gateway: None,
                refund_error_message: None,
                refund_arn: None,
                refund_error_code: None,
                modified_at: common_utils::date_time::now(),
                processor_refund_data: None,
                unified_code: None,
                unified_message: None,
                issuer_error_code: None,
                issuer_error_message: None,
            },
            RefundUpdate::StatusUpdate {
                connector_refund_id,
                sent_to_gateway,
                refund_status,
                updated_by,
                processor_refund_data,
            } => Self {
                connector_refund_id,
                sent_to_gateway: Some(sent_to_gateway),
                refund_status: Some(refund_status),
                updated_by,
                processor_refund_data,
                refund_error_message: None,
                refund_arn: None,
                metadata: None,
                refund_reason: None,
                refund_error_code: None,
                modified_at: common_utils::date_time::now(),
                unified_code: None,
                unified_message: None,
                issuer_error_code: None,
                issuer_error_message: None,
            },
            RefundUpdate::ErrorUpdate {
                refund_status,
                refund_error_message,
                refund_error_code,
                unified_code,
                unified_message,
                updated_by,
                connector_refund_id,
                processor_refund_data,
                issuer_error_code,
                issuer_error_message,
            } => Self {
                refund_status,
                refund_error_message,
                refund_error_code,
                updated_by,
                connector_refund_id,
                processor_refund_data,
                sent_to_gateway: None,
                refund_arn: None,
                metadata: None,
                refund_reason: None,
                modified_at: common_utils::date_time::now(),
                unified_code,
                unified_message,
                issuer_error_code,
                issuer_error_message,
            },
            RefundUpdate::ManualUpdate {
                refund_status,
                refund_error_message,
                refund_error_code,
                updated_by,
            } => Self {
                refund_status,
                refund_error_message,
                refund_error_code,
                updated_by,
                connector_refund_id: None,
                sent_to_gateway: None,
                refund_arn: None,
                metadata: None,
                refund_reason: None,
                modified_at: common_utils::date_time::now(),
                processor_refund_data: None,
                unified_code: None,
                unified_message: None,
                issuer_error_code: None,
                issuer_error_message: None,
            },
        }
    }
}

#[cfg(feature = "v2")]
impl From<RefundUpdate> for RefundUpdateInternal {
    fn from(refund_update: RefundUpdate) -> Self {
        match refund_update {
            RefundUpdate::Update {
                connector_refund_id,
                refund_status,
                sent_to_gateway,
                refund_error_message,
                refund_arn,
                updated_by,
                processor_refund_data,
            } => Self {
                connector_refund_id: Some(connector_refund_id),
                refund_status: Some(refund_status),
                sent_to_gateway: Some(sent_to_gateway),
                refund_error_message,
                refund_arn: Some(refund_arn),
                updated_by,
                processor_refund_data,
                metadata: None,
                refund_reason: None,
                refund_error_code: None,
                modified_at: common_utils::date_time::now(),
                unified_code: None,
                unified_message: None,
            },
            RefundUpdate::MetadataAndReasonUpdate {
                metadata,
                reason,
                updated_by,
            } => Self {
                metadata,
                refund_reason: reason,
                updated_by,
                connector_refund_id: None,
                refund_status: None,
                sent_to_gateway: None,
                refund_error_message: None,
                refund_arn: None,
                refund_error_code: None,
                modified_at: common_utils::date_time::now(),
                processor_refund_data: None,
                unified_code: None,
                unified_message: None,
            },
            RefundUpdate::StatusUpdate {
                connector_refund_id,
                sent_to_gateway,
                refund_status,
                updated_by,
                processor_refund_data,
            } => Self {
                connector_refund_id,
                sent_to_gateway: Some(sent_to_gateway),
                refund_status: Some(refund_status),
                updated_by,
                processor_refund_data,
                refund_error_message: None,
                refund_arn: None,
                metadata: None,
                refund_reason: None,
                refund_error_code: None,
                modified_at: common_utils::date_time::now(),
                unified_code: None,
                unified_message: None,
            },
            RefundUpdate::ErrorUpdate {
                refund_status,
                refund_error_message,
                refund_error_code,
                unified_code,
                unified_message,
                updated_by,
                connector_refund_id,
                processor_refund_data,
            } => Self {
                refund_status,
                refund_error_message,
                refund_error_code,
                updated_by,
                connector_refund_id,
                processor_refund_data,
                sent_to_gateway: None,
                refund_arn: None,
                metadata: None,
                refund_reason: None,
                modified_at: common_utils::date_time::now(),
                unified_code,
                unified_message,
            },
            RefundUpdate::ManualUpdate {
                refund_status,
                refund_error_message,
                refund_error_code,
                updated_by,
            } => Self {
                refund_status,
                refund_error_message,
                refund_error_code,
                updated_by,
                connector_refund_id: None,
                sent_to_gateway: None,
                refund_arn: None,
                metadata: None,
                refund_reason: None,
                modified_at: common_utils::date_time::now(),
                processor_refund_data: None,
                unified_code: None,
                unified_message: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl RefundUpdate {
    pub fn apply_changeset(self, source: Refund) -> Refund {
        let RefundUpdateInternal {
            connector_refund_id,
            refund_status,
            sent_to_gateway,
            refund_error_message,
            refund_arn,
            metadata,
            refund_reason,
            refund_error_code,
            updated_by,
            modified_at: _,
            processor_refund_data,
            unified_code,
            unified_message,
            issuer_error_code,
            issuer_error_message,
        } = self.into();
        Refund {
            connector_refund_id: connector_refund_id.or(source.connector_refund_id),
            refund_status: refund_status.unwrap_or(source.refund_status),
            sent_to_gateway: sent_to_gateway.unwrap_or(source.sent_to_gateway),
            refund_error_message: refund_error_message.or(source.refund_error_message),
            refund_error_code: refund_error_code.or(source.refund_error_code),
            refund_arn: refund_arn.or(source.refund_arn),
            metadata: metadata.or(source.metadata),
            refund_reason: refund_reason.or(source.refund_reason),
            updated_by,
            modified_at: common_utils::date_time::now(),
            processor_refund_data: processor_refund_data.or(source.processor_refund_data),
            unified_code: unified_code.or(source.unified_code),
            unified_message: unified_message.or(source.unified_message),
            issuer_error_code: issuer_error_code.or(source.issuer_error_code),
            issuer_error_message: issuer_error_message.or(source.issuer_error_message),
            ..source
        }
    }
}

#[cfg(feature = "v2")]
impl RefundUpdate {
    pub fn apply_changeset(self, source: Refund) -> Refund {
        let RefundUpdateInternal {
            connector_refund_id,
            refund_status,
            sent_to_gateway,
            refund_error_message,
            refund_arn,
            metadata,
            refund_reason,
            refund_error_code,
            updated_by,
            modified_at: _,
            processor_refund_data,
            unified_code,
            unified_message,
        } = self.into();
        Refund {
            connector_refund_id: connector_refund_id.or(source.connector_refund_id),
            refund_status: refund_status.unwrap_or(source.refund_status),
            sent_to_gateway: sent_to_gateway.unwrap_or(source.sent_to_gateway),
            refund_error_message: refund_error_message.or(source.refund_error_message),
            refund_error_code: refund_error_code.or(source.refund_error_code),
            refund_arn: refund_arn.or(source.refund_arn),
            metadata: metadata.or(source.metadata),
            refund_reason: refund_reason.or(source.refund_reason),
            updated_by,
            modified_at: common_utils::date_time::now(),
            processor_refund_data: processor_refund_data.or(source.processor_refund_data),
            unified_code: unified_code.or(source.unified_code),
            unified_message: unified_message.or(source.unified_message),
            ..source
        }
    }

    pub fn build_error_update_for_unified_error_and_message(
        unified_error_object: (String, String),
        refund_error_message: Option<String>,
        refund_error_code: Option<String>,
        storage_scheme: &storage_enums::MerchantStorageScheme,
    ) -> Self {
        let (unified_code, unified_message) = unified_error_object;

        Self::ErrorUpdate {
            refund_status: Some(storage_enums::RefundStatus::Failure),
            refund_error_message,
            refund_error_code,
            updated_by: storage_scheme.to_string(),
            connector_refund_id: None,
            processor_refund_data: None,
            unified_code: Some(unified_code),
            unified_message: Some(unified_message),
        }
    }

    pub fn build_error_update_for_integrity_check_failure(
        integrity_check_failed_fields: String,
        connector_refund_id: Option<ConnectorTransactionId>,
        storage_scheme: &storage_enums::MerchantStorageScheme,
    ) -> Self {
        Self::ErrorUpdate {
            refund_status: Some(storage_enums::RefundStatus::ManualReview),
            refund_error_message: Some(format!(
                "Integrity Check Failed! as data mismatched for fields {integrity_check_failed_fields}"
            )),
            refund_error_code: Some("IE".to_string()),
            updated_by: storage_scheme.to_string(),
            connector_refund_id: connector_refund_id.clone(),
            processor_refund_data: connector_refund_id.and_then(|x| x.extract_hashed_data()),
            unified_code: None,
            unified_message: None,
        }
    }

    pub fn build_refund_update(
        connector_refund_id: ConnectorTransactionId,
        refund_status: storage_enums::RefundStatus,
        storage_scheme: &storage_enums::MerchantStorageScheme,
    ) -> Self {
        Self::Update {
            connector_refund_id: connector_refund_id.clone(),
            refund_status,
            sent_to_gateway: true,
            refund_error_message: None,
            refund_arn: "".to_string(),
            updated_by: storage_scheme.to_string(),
            processor_refund_data: connector_refund_id.extract_hashed_data(),
        }
    }

    pub fn build_error_update_for_refund_failure(
        refund_status: Option<storage_enums::RefundStatus>,
        refund_error_message: Option<String>,
        refund_error_code: Option<String>,
        storage_scheme: &storage_enums::MerchantStorageScheme,
    ) -> Self {
        Self::ErrorUpdate {
            refund_status,
            refund_error_message,
            refund_error_code,
            updated_by: storage_scheme.to_string(),
            connector_refund_id: None,
            processor_refund_data: None,
            unified_code: None,
            unified_message: None,
        }
    }
}

#[cfg(feature = "v1")]
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct RefundCoreWorkflow {
    pub refund_internal_reference_id: String,
    pub connector_transaction_id: ConnectorTransactionId,
    pub merchant_id: id_type::MerchantId,
    pub payment_id: id_type::PaymentId,
    pub processor_transaction_data: Option<String>,
}

#[cfg(feature = "v2")]
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct RefundCoreWorkflow {
    pub refund_id: id_type::GlobalRefundId,
    pub connector_transaction_id: ConnectorTransactionId,
    pub merchant_id: id_type::MerchantId,
    pub payment_id: id_type::GlobalPaymentId,
    pub processor_transaction_data: Option<String>,
}

#[cfg(feature = "v1")]
impl common_utils::events::ApiEventMetric for Refund {
    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
        Some(common_utils::events::ApiEventsType::Refund {
            payment_id: Some(self.payment_id.clone()),
            refund_id: self.refund_id.clone(),
        })
    }
}

#[cfg(feature = "v2")]
impl common_utils::events::ApiEventMetric for Refund {
    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
        Some(common_utils::events::ApiEventsType::Refund {
            payment_id: Some(self.payment_id.clone()),
            refund_id: self.id.clone(),
        })
    }
}

impl ConnectorTransactionIdTrait for Refund {
    fn get_optional_connector_refund_id(&self) -> Option<&String> {
        match self
            .connector_refund_id
            .as_ref()
            .map(|refund_id| refund_id.get_txn_id(self.processor_refund_data.as_ref()))
            .transpose()
        {
            Ok(refund_id) => refund_id,

            // In case hashed data is missing from DB, use the hashed ID as connector transaction ID
            Err(_) => self
                .connector_refund_id
                .as_ref()
                .map(|txn_id| txn_id.get_id()),
        }
    }

    fn get_connector_transaction_id(&self) -> &String {
        match self
            .connector_transaction_id
            .get_txn_id(self.processor_transaction_data.as_ref())
        {
            Ok(txn_id) => txn_id,

            // In case hashed data is missing from DB, use the hashed ID as connector transaction ID
            Err(_) => self.connector_transaction_id.get_id(),
        }
    }
}

mod tests {
    #[test]
    fn test_backwards_compatibility() {
        let serialized_refund = r#"{
    "internal_reference_id": "internal_ref_123",
    "refund_id": "refund_456",
    "payment_id": "payment_789",
    "merchant_id": "merchant_123",
    "connector_transaction_id": "connector_txn_789",
    "connector": "stripe",
    "connector_refund_id": null,
    "external_reference_id": null,
    "refund_type": "instant_refund",
    "total_amount": 10000,
    "currency": "USD",
    "refund_amount": 9500,
    "refund_status": "Success",
    "sent_to_gateway": true,
    "refund_error_message": null,
    "metadata": null,
    "refund_arn": null,
    "created_at": "2024-02-26T12:00:00Z",
    "updated_at": "2024-02-26T12:00:00Z",
    "description": null,
    "attempt_id": "attempt_123",
    "refund_reason": null,
    "refund_error_code": null,
    "profile_id": null,
    "updated_by": "admin",
    "merchant_connector_id": null,
    "charges": null,
    "connector_transaction_data": null
    "unified_code": null,
    "unified_message": null,
    "processor_transaction_data": null,
}"#;
        let deserialized = serde_json::from_str::<super::Refund>(serialized_refund);

        assert!(deserialized.is_ok());
    }
}


            | Self::CNY
            | Self::COP
            | Self::CRC
            | Self::CUC
            | Self::CUP
            | Self::CVE
            | Self::CZK
            | Self::DJF
            | Self::DKK
            | Self::DOP
            | Self::DZD
            | Self::EGP
            | Self::ERN
            | Self::ETB
            | Self::EUR
            | Self::FJD
            | Self::FKP
            | Self::GBP
            | Self::GEL
            | Self::GHS
            | Self::GIP
            | Self::GMD
            | Self::GNF
            | Self::GTQ
            | Self::GYD
            | Self::HKD
            | Self::HNL
            | Self::HRK
            | Self::HTG
            | Self::HUF
            | Self::IDR
            | Self::ILS
            | Self::INR
            | Self::IRR
            | Self::ISK
            | Self::JMD
            | Self::JPY
            | Self::KES
            | Self::KGS
            | Self::KHR
            | Self::KMF
            | Self::KPW
            | Self::KRW
            | Self::KYD
            | Self::KZT
            | Self::LAK
            | Self::LBP
            | Self::LKR
            | Self::LRD
            | Self::LSL
            | Self::MAD
            | Self::MDL
            | Self::MGA
            | Self::MKD
            | Self::MMK
            | Self::MNT
            | Self::MOP
            | Self::MRU
            | Self::MUR
            | Self::MVR
            | Self::MWK
            | Self::MXN
            | Self::MYR
            | Self::MZN
            | Self::NAD
            | Self::NGN
            | Self::NIO
            | Self::NOK
            | Self::NPR
            | Self::NZD
            | Self::PAB
            | Self::PEN
            | Self::PGK
            | Self::PHP
            | Self::PKR
            | Self::PLN
            | Self::PYG
            | Self::QAR
            | Self::RON
            | Self::RSD
            | Self::RUB
            | Self::RWF
            | Self::SAR
            | Self::SBD
            | Self::SCR
            | Self::SDG
            | Self::SEK
            | Self::SGD
            | Self::SHP
            | Self::SLE
            | Self::SLL
            | Self::SOS
            | Self::SRD
            | Self::SSP
            | Self::STD
            | Self::STN
            | Self::SVC
            | Self::SYP
            | Self::SZL
            | Self::THB
            | Self::TJS
            | Self::TMT
            | Self::TOP
            | Self::TRY
            | Self::TTD
            | Self::TWD
            | Self::TZS
            | Self::UAH
            | Self::UGX
            | Self::USD
            | Self::UYU
            | Self::UZS
            | Self::VES
            | Self::VND
            | Self::VUV
            | Self::WST
            | Self::XAF
            | Self::XCD
            | Self::XPF
            | Self::XOF
            | Self::YER
            | Self::ZAR
            | Self::ZMW
            | Self::ZWL => false,
        }
    }

    pub fn is_four_decimal_currency(self) -> bool {
        match self {
            Self::CLF => true,
            Self::AED
            | Self::AFN
            | Self::ALL
            | Self::AMD
            | Self::AOA
            | Self::ANG
            | Self::ARS
            | Self::AUD
            | Self::AWG
            | Self::AZN
            | Self::BAM
            | Self::BBD
            | Self::BDT
            | Self::BGN
            | Self::BHD
            | Self::BIF
            | Self::BMD
            | Self::BND
            | Self::BOB
            | Self::BRL
            | Self::BSD
            | Self::BTN
            | Self::BWP
            | Self::BYN
            | Self::BZD
            | Self::CAD
            | Self::CDF
            | Self::CHF
            | Self::CLP
            | Self::CNY
            | Self::COP
            | Self::CRC
            | Self::CUC
            | Self::CUP
            | Self::CVE
            | Self::CZK
            | Self::DJF
            | Self::DKK
            | Self::DOP
            | Self::DZD
            | Self::EGP
            | Self::ERN
            | Self::ETB
            | Self::EUR
            | Self::FJD
            | Self::FKP
            | Self::GBP
            | Self::GEL
            | Self::GHS
            | Self::GIP
            | Self::GMD
            | Self::GNF
            | Self::GTQ
            | Self::GYD
            | Self::HKD
            | Self::HNL
            | Self::HRK
            | Self::HTG
            | Self::HUF
            | Self::IDR
            | Self::ILS
            | Self::INR
            | Self::IQD
            | Self::IRR
            | Self::ISK
            | Self::JMD
            | Self::JOD
            | Self::JPY
            | Self::KES
            | Self::KGS
            | Self::KHR
            | Self::KMF
            | Self::KPW
            | Self::KRW
            | Self::KWD
            | Self::KYD
            | Self::KZT
            | Self::LAK
            | Self::LBP
            | Self::LKR
            | Self::LRD
            | Self::LSL
            | Self::LYD
            | Self::MAD
            | Self::MDL
            | Self::MGA
            | Self::MKD
            | Self::MMK
            | Self::MNT
            | Self::MOP
            | Self::MRU
            | Self::MUR
            | Self::MVR
            | Self::MWK
            | Self::MXN
            | Self::MYR
            | Self::MZN
            | Self::NAD
            | Self::NGN
            | Self::NIO
            | Self::NOK
            | Self::NPR
            | Self::NZD
            | Self::OMR
            | Self::PAB
            | Self::PEN
            | Self::PGK
            | Self::PHP
            | Self::PKR
            | Self::PLN
            | Self::PYG
            | Self::QAR
            | Self::RON
            | Self::RSD
            | Self::RUB
            | Self::RWF
            | Self::SAR
            | Self::SBD
            | Self::SCR
            | Self::SDG
            | Self::SEK
            | Self::SGD
            | Self::SHP
            | Self::SLE
            | Self::SLL
            | Self::SOS
            | Self::SRD
            | Self::SSP
            | Self::STD
            | Self::STN
            | Self::SVC
            | Self::SYP
            | Self::SZL
            | Self::THB
            | Self::TJS
            | Self::TMT
            | Self::TND
            | Self::TOP
            | Self::TRY
            | Self::TTD
            | Self::TWD
            | Self::TZS
            | Self::UAH
            | Self::UGX
            | Self::USD
            | Self::UYU
            | Self::UZS
            | Self::VES
            | Self::VND
            | Self::VUV
            | Self::WST
            | Self::XAF
            | Self::XCD
            | Self::XPF
            | Self::XOF
            | Self::YER
            | Self::ZAR
            | Self::ZMW
            | Self::ZWL => false,
        }
    }

    pub fn number_of_digits_after_decimal_point(self) -> u8 {
        if self.is_zero_decimal_currency() {
            0
        } else if self.is_three_decimal_currency() {
            3
        } else if self.is_four_decimal_currency() {
            4
        } else {
            2
        }
    }
}

#[derive(
    Clone,
    Copy,
    Debug,
    Hash,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum EventClass {
    Payments,
    Refunds,
    Disputes,
    Mandates,
    #[cfg(feature = "payouts")]
    Payouts,
}

impl EventClass {
    #[inline]
    pub fn event_types(self) -> HashSet<EventType> {
        match self {
            Self::Payments => HashSet::from([
                EventType::PaymentSucceeded,
                EventType::PaymentFailed,
                EventType::PaymentProcessing,
                EventType::PaymentCancelled,
                EventType::PaymentCancelledPostCapture,
                EventType::PaymentAuthorized,
                EventType::PaymentCaptured,
                EventType::PaymentExpired,
                EventType::ActionRequired,
            ]),
            Self::Refunds => HashSet::from([EventType::RefundSucceeded, EventType::RefundFailed]),
            Self::Disputes => HashSet::from([
                EventType::DisputeOpened,
                EventType::DisputeExpired,
                EventType::DisputeAccepted,
                EventType::DisputeCancelled,
                EventType::DisputeChallenged,
                EventType::DisputeWon,
                EventType::DisputeLost,
            ]),
            Self::Mandates => HashSet::from([EventType::MandateActive, EventType::MandateRevoked]),
            #[cfg(feature = "payouts")]
            Self::Payouts => HashSet::from([
                EventType::PayoutSuccess,
                EventType::PayoutFailed,
                EventType::PayoutInitiated,
                EventType::PayoutProcessing,
                EventType::PayoutCancelled,
                EventType::PayoutExpired,
                EventType::PayoutReversed,
            ]),
        }
    }
}

#[derive(
    Clone,
    Copy,
    Debug,
    Hash,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
// Reminder: Whenever an EventType variant is added or removed, make sure to update the `event_types` method in `EventClass`
pub enum EventType {
    /// Authorize + Capture success
    PaymentSucceeded,
    /// Authorize + Capture failed
    PaymentFailed,
    PaymentProcessing,
    PaymentCancelled,
    PaymentCancelledPostCapture,
    PaymentAuthorized,
    PaymentPartiallyAuthorized,
    PaymentCaptured,
    PaymentExpired,
    ActionRequired,
    RefundSucceeded,
    RefundFailed,
    DisputeOpened,
    DisputeExpired,
    DisputeAccepted,
    DisputeCancelled,
    DisputeChallenged,
    DisputeWon,
    DisputeLost,
    MandateActive,
    MandateRevoked,
    #[cfg(feature = "payouts")]
    PayoutSuccess,
    #[cfg(feature = "payouts")]
    PayoutFailed,
    #[cfg(feature = "payouts")]
    PayoutInitiated,
    #[cfg(feature = "payouts")]
    PayoutProcessing,
    #[cfg(feature = "payouts")]
    PayoutCancelled,
    #[cfg(feature = "payouts")]
    PayoutExpired,
    #[cfg(feature = "payouts")]
    PayoutReversed,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum WebhookDeliveryAttempt {
    InitialAttempt,
    AutomaticRetry,
    ManualRetry,
}

// TODO: This decision about using KV mode or not,
// should be taken at a top level rather than pushing it down to individual functions via an enum.
#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum MerchantStorageScheme {
    #[default]
    PostgresOnly,
    RedisKv,
}

/// Represents the overall status of a payment intent.
/// The status transitions through various states depending on the payment method, confirmation, capture method, and any subsequent actions (like customer authentication or manual capture).
#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    ToSchema,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumIter,
    strum::EnumString,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum IntentStatus {
    /// The payment has succeeded. Refunds and disputes can be initiated.
    /// Manual retries are not allowed to be performed.
    Succeeded,
    /// The payment has failed. Refunds and disputes cannot be initiated.
    /// This payment can be retried manually with a new payment attempt.
    Failed,
    /// This payment has been cancelled.
    Cancelled,
    /// This payment has been cancelled post capture.
    CancelledPostCapture,
    /// This payment is still being processed by the payment processor.
    /// The status update might happen through webhooks or polling with the connector.
    Processing,
    /// The payment is waiting on some action from the customer.
    RequiresCustomerAction,
    /// The payment is waiting on some action from the merchant
    /// This would be in case of manual fraud approval
    RequiresMerchantAction,
    /// The payment is waiting to be confirmed with the payment method by the customer.
    RequiresPaymentMethod,
    #[default]
    RequiresConfirmation,
    /// The payment has been authorized, and it waiting to be captured.
    RequiresCapture,
    /// The payment has been captured partially. The remaining amount is cannot be captured.
    PartiallyCaptured,
    /// The payment has been captured partially and the remaining amount is capturable
    PartiallyCapturedAndCapturable,
    /// The payment has been authorized for a partial amount and requires capture
    PartiallyAuthorizedAndRequiresCapture,
    /// There has been a discrepancy between the amount/currency sent in the request and the amount/currency received by the processor
    Conflicted,
    /// The payment expired before it could be captured.
    Expired,
}

impl IntentStatus {
    /// Indicates whether the payment intent is in terminal state or not
    pub fn is_in_terminal_state(self) -> bool {
        match self {
            Self::Succeeded
            | Self::Failed
            | Self::Cancelled
            | Self::CancelledPostCapture
            | Self::PartiallyCaptured
            | Self::Expired => true,
            Self::Processing
            | Self::RequiresCustomerAction
            | Self::RequiresMerchantAction
            | Self::RequiresPaymentMethod
            | Self::RequiresConfirmation
            | Self::RequiresCapture
            | Self::PartiallyCapturedAndCapturable
            | Self::PartiallyAuthorizedAndRequiresCapture
            | Self::Conflicted => false,
        }
    }

    /// Indicates whether the syncing with the connector should be allowed or not
    pub fn should_force_sync_with_connector(self) -> bool {
        match self {
            // Confirm has not happened yet
            Self::RequiresConfirmation
            | Self::RequiresPaymentMethod
            // Once the status is success, failed or cancelled need not force sync with the connector
            | Self::Succeeded
            | Self::Failed
            | Self::Cancelled
            | Self::CancelledPostCapture
            |  Self::PartiallyCaptured
            |  Self::RequiresCapture | Self::Conflicted | Self::Expired=> false,
            Self::Processing
            | Self::RequiresCustomerAction
            | Self::RequiresMerchantAction
            | Self::PartiallyCapturedAndCapturable
            | Self::PartiallyAuthorizedAndRequiresCapture => true,
        }
    }
}

/// Specifies how the payment method can be used for future payments.
/// - `off_session`: The payment method can be used for future payments when the customer is not present.
/// - `on_session`: The payment method is intended for use only when the customer is present during checkout.
/// If omitted, defaults to `on_session`.
#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum FutureUsage {
    OffSession,
    #[default]
    OnSession,
}

impl FutureUsage {
    /// Indicates whether the payment method should be saved for future use or not
    pub fn is_off_session(self) -> bool {
        match self {
            Self::OffSession => true,
            Self::OnSession => false,
        }
    }
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum PaymentMethodIssuerCode {
    JpHdfc,
    JpIcici,
    JpGooglepay,
    JpApplepay,
    JpPhonepay,
    JpWechat,
    JpSofort,
    JpGiropay,
    JpSepa,
    JpBacs,
}

/// Payment Method Status
#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    Hash,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum PaymentMethodStatus {
    /// Indicates that the payment method is active and can be used for payments.
    Active,
    /// Indicates that the payment method is not active and hence cannot be used for payments.
    Inactive,
    /// Indicates that the payment method is awaiting some data or action before it can be marked
    /// as 'active'.
    Processing,
    /// Indicates that the payment method is awaiting some data before changing state to active
    AwaitingData,
}

impl From<AttemptStatus> for PaymentMethodStatus {
    fn from(attempt_status: AttemptStatus) -> Self {
        match attempt_status {
            AttemptStatus::Failure
            | AttemptStatus::Voided
            | AttemptStatus::VoidedPostCharge
            | AttemptStatus::Started
            | AttemptStatus::Pending
            | AttemptStatus::Unresolved
            | AttemptStatus::CodInitiated
            | AttemptStatus::Authorizing
            | AttemptStatus::VoidInitiated
            | AttemptStatus::AuthorizationFailed
            | AttemptStatus::RouterDeclined
            | AttemptStatus::AuthenticationSuccessful
            | AttemptStatus::PaymentMethodAwaited
            | AttemptStatus::AuthenticationFailed
            | AttemptStatus::AuthenticationPending
            | AttemptStatus::CaptureInitiated
            | AttemptStatus::CaptureFailed
            | AttemptStatus::VoidFailed
            | AttemptStatus::AutoRefunded
            | AttemptStatus::PartialCharged
            | AttemptStatus::PartialChargedAndChargeable
            | AttemptStatus::PartiallyAuthorized
            | AttemptStatus::ConfirmationAwaited
            | AttemptStatus::DeviceDataCollectionPending
            | AttemptStatus::IntegrityFailure
            | AttemptStatus::Expired => Self::Inactive,
            AttemptStatus::Charged | AttemptStatus::Authorized => Self::Active,
        }
    }
}

/// To indicate the type of payment experience that the customer would go through
#[derive(
    Eq,
    strum::EnumString,
    PartialEq,
    Hash,
    Copy,
    Clone,
    Debug,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    ToSchema,
    Default,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum PaymentExperience {
    /// The URL to which the customer needs to be redirected for completing the payment.
    #[default]
    RedirectToUrl,
    /// Contains the data for invoking the sdk client for completing the payment.
    InvokeSdkClient,
    /// The QR code data to be displayed to the customer.
    DisplayQrCode,
    /// Contains data to finish one click payment.
    OneClick,
    /// Redirect customer to link wallet
    LinkWallet,
    /// Contains the data for invoking the sdk client for completing the payment.
    InvokePaymentApp,
    /// Contains the data for displaying wait screen
    DisplayWaitScreen,
    /// Represents that otp needs to be collect and contains if consent is required
    CollectOtp,
}

#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, strum::Display)]
#[serde(rename_all = "lowercase")]
pub enum SamsungPayCardBrand {
    Visa,
    MasterCard,
    Amex,
    Discover,
    Unknown,
}

/// Indicates the sub type of payment method. Eg: 'google_pay' & 'apple_pay' for wallets.
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    Ord,
    Hash,
    PartialOrd,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PaymentMethodType {
    Ach,
    Affirm,
    AfterpayClearpay,
    Alfamart,
    AliPay,
    AliPayHk,
    Alma,
    AmazonPay,
    Paysera,
    ApplePay,
    Atome,
    Bacs,
    BancontactCard,
    Becs,
    Benefit,
    Bizum,
    Blik,
    Bluecode,
    Boleto,
    BcaBankTransfer,
    BniVa,
    Breadpay,
    BriVa,
    BhnCardNetwork,
    #[cfg(feature = "v2")]
    Card,
    CardRedirect,
    CimbVa,
    #[serde(rename = "classic")]
    ClassicReward,
    Credit,
    CryptoCurrency,
    Cashapp,
    Dana,
    DanamonVa,
    Debit,
    DuitNow,
    Efecty,
    Eft,
    Eps,
    Flexiti,
    Fps,
    Evoucher,
    Giropay,
    Givex,
    GooglePay,
    GoPay,
    Gcash,
    Ideal,
    Interac,
    Indomaret,
    Klarna,
    KakaoPay,
    LocalBankRedirect,
    MandiriVa,
    Knet,
    MbWay,
    MobilePay,
    Momo,
    MomoAtm,
    Multibanco,
    OnlineBankingThailand,
    OnlineBankingCzechRepublic,
    OnlineBankingFinland,
    OnlineBankingFpx,
    OnlineBankingPoland,
    OnlineBankingSlovakia,
    Oxxo,
    PagoEfectivo,
    PermataBankTransfer,
    OpenBankingUk,
    PayBright,
    Paypal,
    Paze,
    Pix,
    PaySafeCard,
    Przelewy24,
    PromptPay,
    Pse,
    RedCompra,
    RedPagos,
    SamsungPay,
    Sepa,
    SepaBankTransfer,
    Skrill,
    Sofort,
    Swish,
    TouchNGo,
    Trustly,
    Twint,
    UpiCollect,
    UpiIntent,
    Vipps,
    VietQr,
    Venmo,
    Walley,
    WeChatPay,
    SevenEleven,
    Lawson,
    MiniStop,
    FamilyMart,
    Seicomart,
    PayEasy,
    LocalBankTransfer,
    Mifinity,
    #[serde(rename = "open_banking_pis")]
    OpenBankingPIS,
    DirectCarrierBilling,
    InstantBankTransfer,
    InstantBankTransferFinland,
    InstantBankTransferPoland,
    RevolutPay,
    IndonesianBankTransfer,
}

impl PaymentMethodType {
    pub fn should_check_for_customer_saved_payment_method_type(self) -> bool {
        matches!(
            self,
            Self::ApplePay | Self::GooglePay | Self::SamsungPay | Self::Paypal | Self::Klarna
        )
    }
    pub fn to_display_name(&self) -> String {
        let display_name = match self {
            Self::Ach => "ACH Direct Debit",
            Self::Bacs => "BACS Direct Debit",
            Self::Affirm => "Affirm",
            Self::AfterpayClearpay => "Afterpay Clearpay",
            Self::Alfamart => "Alfamart",
            Self::AliPay => "Alipay",
            Self::AliPayHk => "AlipayHK",
            Self::Alma => "Alma",
            Self::AmazonPay => "Amazon Pay",
            Self::Paysera => "Paysera",
            Self::ApplePay => "Apple Pay",
            Self::Atome => "Atome",
            Self::BancontactCard => "Bancontact Card",
            Self::Becs => "BECS Direct Debit",
            Self::Benefit => "Benefit",
            Self::Bizum => "Bizum",
            Self::Blik => "BLIK",
            Self::Bluecode => "Bluecode",
            Self::Boleto => "Boleto Bancário",
            Self::BcaBankTransfer => "BCA Bank Transfer",
            Self::BniVa => "BNI Virtual Account",
            Self::Breadpay => "Breadpay",
            Self::BriVa => "BRI Virtual Account",
            Self::BhnCardNetwork => "BHN Card Network",
            Self::CardRedirect => "Card Redirect",
            Self::CimbVa => "CIMB Virtual Account",
            Self::ClassicReward => "Classic Reward",
            #[cfg(feature = "v2")]
            Self::Card => "Card",
            Self::Credit => "Credit Card",
            Self::CryptoCurrency => "Crypto",
            Self::Cashapp => "Cash App",
            Self::Dana => "DANA",
            Self::DanamonVa => "Danamon Virtual Account",
            Self::Debit => "Debit Card",
            Self::DuitNow => "DuitNow",
            Self::Efecty => "Efecty",
            Self::Eft => "EFT",
            Self::Eps => "EPS",
            Self::Flexiti => "Flexiti",
            Self::Fps => "FPS",
            Self::Evoucher => "Evoucher",
            Self::Giropay => "Giropay",
            Self::Givex => "Givex",
            Self::GooglePay => "Google Pay",
            Self::GoPay => "GoPay",
            Self::Gcash => "GCash",
            Self::Ideal => "iDEAL",
            Self::Interac => "Interac",
            Self::Indomaret => "Indomaret",
            Self::InstantBankTransfer => "Instant Bank Transfer",
            Self::InstantBankTransferFinland => "Instant Bank Transfer Finland",
            Self::InstantBankTransferPoland => "Instant Bank Transfer Poland",
            Self::Klarna => "Klarna",
            Self::KakaoPay => "KakaoPay",
            Self::LocalBankRedirect => "Local Bank Redirect",
            Self::MandiriVa => "Mandiri Virtual Account",
            Self::Knet => "KNET",
            Self::MbWay => "MB WAY",
            Self::MobilePay => "MobilePay",
            Self::Momo => "MoMo",
            Self::MomoAtm => "MoMo ATM",
            Self::Multibanco => "Multibanco",
            Self::OnlineBankingThailand => "Online Banking Thailand",
            Self::OnlineBankingCzechRepublic => "Online Banking Czech Republic",
            Self::OnlineBankingFinland => "Online Banking Finland",
            Self::OnlineBankingFpx => "Online Banking FPX",
            Self::OnlineBankingPoland => "Online Banking Poland",
            Self::OnlineBankingSlovakia => "Online Banking Slovakia",
            Self::Oxxo => "OXXO",
            Self::PagoEfectivo => "PagoEfectivo",
            Self::PermataBankTransfer => "Permata Bank Transfer",
            Self::OpenBankingUk => "Open Banking UK",
            Self::PayBright => "PayBright",
            Self::Paypal => "PayPal",
            Self::Paze => "Paze",
            Self::Pix => "Pix",
            Self::PaySafeCard => "PaySafeCard",
            Self::Przelewy24 => "Przelewy24",
            Self::PromptPay => "PromptPay",
            Self::Pse => "PSE",
            Self::RedCompra => "RedCompra",
            Self::RedPagos => "RedPagos",
            Self::SamsungPay => "Samsung Pay",
            Self::Sepa => "SEPA Direct Debit",
            Self::SepaBankTransfer => "SEPA Bank Transfer",
            Self::Sofort => "Sofort",
            Self::Skrill => "Skrill",
            Self::Swish => "Swish",
            Self::TouchNGo => "Touch 'n Go",
            Self::Trustly => "Trustly",
            Self::Twint => "TWINT",
            Self::UpiCollect => "UPI Collect",
            Self::UpiIntent => "UPI Intent",
            Self::Vipps => "Vipps",
            Self::VietQr => "VietQR",
            Self::Venmo => "Venmo",
            Self::Walley => "Walley",
            Self::WeChatPay => "WeChat Pay",
            Self::SevenEleven => "7-Eleven",
            Self::Lawson => "Lawson",
            Self::MiniStop => "Mini Stop",
            Self::FamilyMart => "FamilyMart",
            Self::Seicomart => "Seicomart",
            Self::PayEasy => "PayEasy",
            Self::LocalBankTransfer => "Local Bank Transfer",
            Self::Mifinity => "MiFinity",
            Self::OpenBankingPIS => "Open Banking PIS",
            Self::DirectCarrierBilling => "Direct Carrier Billing",
            Self::RevolutPay => "RevolutPay",
            Self::IndonesianBankTransfer => "Indonesian Bank Transfer",
        };
        display_name.to_string()
    }
}

impl masking::SerializableSecret for PaymentMethodType {}

/// Indicates the type of payment method. Eg: 'card', 'wallet', etc.
#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PaymentMethod {
    #[default]
    Card,
    CardRedirect,
    PayLater,
    Wallet,
    BankRedirect,
    BankTransfer,
    Crypto,
    BankDebit,
    Reward,
    RealTimePayment,
    Upi,
    Voucher,
    GiftCard,
    OpenBanking,
    MobilePayment,
}

/// Indicates the gateway system through which the payment is processed.
#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum GatewaySystem {
    #[default]
    Direct,
    UnifiedConnectorService,
}

/// The type of the payment that differentiates between normal and various types of mandate payments. Use 'setup_mandate' in case of zero auth flow.
#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum PaymentType {
    #[default]
    Normal,
    NewMandate,
    SetupMandate,
    RecurringMandate,
}

/// SCA Exemptions types available for authentication
#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum ScaExemptionType {
    #[default]
    LowValue,
    TransactionRiskAnalysis,
}

#[derive(
    Clone,
    Debug,
    Default,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::VariantNames,
    strum::EnumIter,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
/// Describes the channel through which the payment was initiated.
pub enum PaymentChannel {
    #[default]
    Ecommerce,
    MailOrder,
    TelephoneOrder,
    #[serde(untagged)]
    #[strum(default)]
    Other(String),
}

#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    PartialEq,
    serde::Deserialize,
    serde::Serialize,
    strum::Display,
    strum::EnumString,
    ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CtpServiceProvider {
    Visa,
    Mastercard,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    strum::Display,
    strum::EnumString,
    strum::EnumIter,
    serde::Serialize,
    serde::Deserialize,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum RefundStatus {
    #[serde(alias = "Failure")]
    Failure,
    #[serde(alias = "ManualReview")]
    ManualReview,
    #[default]
    #[serde(alias = "Pending")]
    Pending,
    #[serde(alias = "Success")]
    Success,
    #[serde(alias = "TransactionFailure")]
    TransactionFailure,
}

#[derive(
    Clone,
    Copy,
    Debug,
    Default,
    Eq,
    Hash,
    PartialEq,
    strum::Display,
    strum::EnumString,
    strum::EnumIter,
    serde::Serialize,
    serde::Deserialize,
    ToSchema,
)]


pub fn from_storage(attempt: &'a PaymentAttempt) -> Self {
        use masking::PeekInterface;
        let PaymentAttempt {
            payment_id,
            merchant_id,
            amount_details,
            status,
            connector,
            error,
            authentication_type,
            created_at,
            modified_at,
            last_synced,
            cancellation_reason,
            browser_info,
            payment_token,
            connector_metadata,
            payment_experience,
            payment_method_data,
            routing_result,
            preprocessing_step_id,
            multiple_capture_count,
            connector_response_reference_id,
            updated_by,
            redirection_data,
            encoded_data,
            merchant_connector_id,
            external_three_ds_authentication_attempted,
            authentication_connector,
            authentication_id,
            fingerprint_id,
            client_source,
            client_version,
            customer_acceptance,
            profile_id,
            organization_id,
            payment_method_type,
            payment_method_id,
            connector_payment_id,
            payment_method_subtype,
            authentication_applied,
            external_reference_id,
            payment_method_billing_address,
            id,
            connector_token_details,
            card_discovery,
            charges,
            feature_metadata,
            processor_merchant_id,
            created_by,
            connector_request_reference_id,
            network_transaction_id: _,
        } = attempt;

        let (connector_payment_id, connector_payment_data) = connector_payment_id
            .clone()
            .map(types::ConnectorTransactionId::form_id_and_data)
            .map(|(txn_id, txn_data)| (Some(txn_id), txn_data))
            .unwrap_or((None, None));

        Self {
            payment_id,
            merchant_id,
            attempt_id: id,
            status: *status,
            amount: amount_details.get_net_amount(),
            connector: connector.as_ref(),
            error_message: error.as_ref().map(|error_details| &error_details.message),
            surcharge_amount: amount_details.get_surcharge_amount(),
            tax_amount: amount_details.get_tax_on_surcharge(),
            payment_method_id: payment_method_id.as_ref(),
            payment_method: *payment_method_type,
            connector_transaction_id: connector_response_reference_id.as_ref(),
            authentication_type: *authentication_type,
            created_at: created_at.assume_utc(),
            modified_at: modified_at.assume_utc(),
            last_synced: last_synced.map(|i| i.assume_utc()),
            cancellation_reason: cancellation_reason.as_ref(),
            amount_to_capture: amount_details.get_amount_to_capture(),
            browser_info: browser_info.as_ref(),
            error_code: error.as_ref().map(|error_details| &error_details.code),
            connector_metadata: connector_metadata.as_ref().map(|v| v.peek().to_string()),
            payment_experience: payment_experience.as_ref(),
            payment_method_type: payment_method_subtype,
            payment_method_data: payment_method_data.as_ref().map(|v| v.peek().to_string()),
            error_reason: error
                .as_ref()
                .and_then(|error_details| error_details.reason.as_ref()),
            multiple_capture_count: *multiple_capture_count,
            amount_capturable: amount_details.get_amount_capturable(),
            merchant_connector_id: merchant_connector_id.as_ref(),
            net_amount: amount_details.get_net_amount(),
            unified_code: error
                .as_ref()
                .and_then(|error_details| error_details.unified_code.as_ref()),
            unified_message: error
                .as_ref()
                .and_then(|error_details| error_details.unified_message.as_ref()),
            client_source: client_source.as_ref(),
            client_version: client_version.as_ref(),
            profile_id,
            organization_id,
            card_network: payment_method_data
                .as_ref()
                .map(|data| data.peek())
                .and_then(|data| data.as_object())
                .and_then(|pm| pm.get("card"))
                .and_then(|data| data.as_object())
                .and_then(|card| card.get("card_network"))
                .and_then(|network| network.as_str())
                .map(|network| network.to_string()),
            card_discovery: card_discovery.map(|discovery| discovery.to_string()),
            payment_token: payment_token.clone(),
            preprocessing_step_id: preprocessing_step_id.clone(),
            connector_response_reference_id: connector_response_reference_id.clone(),
            updated_by,
            encoded_data: encoded_data.as_ref(),
            external_three_ds_authentication_attempted: *external_three_ds_authentication_attempted,
            authentication_connector: authentication_connector.clone(),
            authentication_id: authentication_id
                .as_ref()
                .map(|id| id.get_string_repr().to_string().clone()),
            fingerprint_id: fingerprint_id.clone(),
            customer_acceptance: customer_acceptance.as_ref(),
            shipping_cost: amount_details.get_shipping_cost(),
            order_tax_amount: amount_details.get_order_tax_amount(),
            charges: charges.clone(),
            processor_merchant_id,
            created_by: created_by.as_ref(),
            payment_method_type_v2: *payment_method_type,
            connector_payment_id: connector_payment_id.as_ref().cloned(),
            payment_method_subtype: *payment_method_subtype,
            routing_result: routing_result.clone(),
            authentication_applied: *authentication_applied,
            external_reference_id: external_reference_id.clone(),
            tax_on_surcharge: amount_details.get_tax_on_surcharge(),
            payment_method_billing_address: payment_method_billing_address
                .as_ref()
                .map(|v| masking::Secret::new(v.get_inner())),
            redirection_data: redirection_data.as_ref(),
            connector_payment_data,
            connector_token_details: connector_token_details.as_ref(),
            feature_metadata: feature_metadata.as_ref(),
            network_advice_code: error
                .as_ref()
                .and_then(|details| details.network_advice_code.clone()),
            network_decline_code: error
                .as_ref()
                .and_then(|details| details.network_decline_code.clone()),
            network_error_message: error
                .as_ref()
                .and_then(|details| details.network_error_message.clone()),
            connector_request_reference_id: connector_request_reference_id.clone(),
        }
    }


impl api::PaymentSession for Paystack {}

impl api::PaymentSession for for Paystack


File: crates/router/src/core/routing/transformers.rs

use api_models::routing::{
    DynamicRoutingAlgorithm, MerchantRoutingAlgorithm, RoutingAlgorithmKind,
    RoutingAlgorithmWrapper, RoutingDictionaryRecord,
};
#[cfg(feature = "v1")]
use api_models::{
    open_router::{OpenRouterDecideGatewayRequest, PaymentInfo, RankingAlgorithm},
    routing::RoutableConnectorChoice,
};
use common_utils::ext_traits::ValueExt;
use diesel_models::{
    enums as storage_enums,
    routing_algorithm::{RoutingAlgorithm, RoutingProfileMetadata},
};
#[cfg(feature = "v1")]
use hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt;
use masking::{PeekInterface, Secret};

use crate::{
    core::{errors, routing},
    types::transformers::{ForeignFrom, ForeignInto, ForeignTryFrom},
};

impl ForeignFrom<RoutingProfileMetadata> for RoutingDictionaryRecord {
    fn foreign_from(value: RoutingProfileMetadata) -> Self {
        Self {
            id: value.algorithm_id,

            profile_id: value.profile_id,
            name: value.name,
            kind: value.kind.foreign_into(),
            description: value.description.unwrap_or_default(),
            created_at: value.created_at.assume_utc().unix_timestamp(),
            modified_at: value.modified_at.assume_utc().unix_timestamp(),
            algorithm_for: Some(value.algorithm_for),
            decision_engine_routing_id: None,
        }
    }
}

impl ForeignFrom<RoutingAlgorithm> for RoutingDictionaryRecord {
    fn foreign_from(value: RoutingAlgorithm) -> Self {
        Self {
            id: value.algorithm_id,

            profile_id: value.profile_id,
            name: value.name,
            kind: value.kind.foreign_into(),
            description: value.description.unwrap_or_default(),
            created_at: value.created_at.assume_utc().unix_timestamp(),
            modified_at: value.modified_at.assume_utc().unix_timestamp(),
            algorithm_for: Some(value.algorithm_for),
            decision_engine_routing_id: value.decision_engine_routing_id,
        }
    }
}

impl ForeignTryFrom<RoutingAlgorithm> for MerchantRoutingAlgorithm {
    type Error = error_stack::Report<errors::ParsingError>;

    fn foreign_try_from(value: RoutingAlgorithm) -> Result<Self, Self::Error> {
        let algorithm: RoutingAlgorithmWrapper = match value.kind {
            diesel_models::enums::RoutingAlgorithmKind::Dynamic => value
                .algorithm_data
                .parse_value::<DynamicRoutingAlgorithm>("RoutingAlgorithmDynamic")
                .map(RoutingAlgorithmWrapper::Dynamic)?,
            _ => value
                .algorithm_data
                .parse_value::<api_models::routing::StaticRoutingAlgorithm>("RoutingAlgorithm")
                .map(RoutingAlgorithmWrapper::Static)?,
        };

        Ok(Self {
            id: value.algorithm_id,
            name: value.name,
            profile_id: value.profile_id,
            description: value.description.unwrap_or_default(),
            algorithm,
            created_at: value.created_at.assume_utc().unix_timestamp(),
            modified_at: value.modified_at.assume_utc().unix_timestamp(),
            algorithm_for: value.algorithm_for,
        })
    }
}

impl ForeignFrom<storage_enums::RoutingAlgorithmKind> for RoutingAlgorithmKind {
    fn foreign_from(value: storage_enums::RoutingAlgorithmKind) -> Self {
        match value {
            storage_enums::RoutingAlgorithmKind::Single => Self::Single,
            storage_enums::RoutingAlgorithmKind::Priority => Self::Priority,
            storage_enums::RoutingAlgorithmKind::VolumeSplit => Self::VolumeSplit,
            storage_enums::RoutingAlgorithmKind::Advanced => Self::Advanced,
            storage_enums::RoutingAlgorithmKind::Dynamic => Self::Dynamic,
            storage_enums::RoutingAlgorithmKind::ThreeDsDecisionRule => Self::ThreeDsDecisionRule,
        }
    }
}

impl ForeignFrom<RoutingAlgorithmKind> for storage_enums::RoutingAlgorithmKind {
    fn foreign_from(value: RoutingAlgorithmKind) -> Self {
        match value {
            RoutingAlgorithmKind::Single => Self::Single,
            RoutingAlgorithmKind::Priority => Self::Priority,
            RoutingAlgorithmKind::VolumeSplit => Self::VolumeSplit,
            RoutingAlgorithmKind::Advanced => Self::Advanced,
            RoutingAlgorithmKind::Dynamic => Self::Dynamic,
            RoutingAlgorithmKind::ThreeDsDecisionRule => Self::ThreeDsDecisionRule,
        }
    }
}

impl From<&routing::TransactionData<'_>> for storage_enums::TransactionType {
    fn from(value: &routing::TransactionData<'_>) -> Self {
        match value {
            routing::TransactionData::Payment(_) => Self::Payment,
            #[cfg(feature = "payouts")]
            routing::TransactionData::Payout(_) => Self::Payout,
        }
    }
}

#[cfg(feature = "v1")]
pub trait OpenRouterDecideGatewayRequestExt {
    fn construct_sr_request(
        attempt: &PaymentAttempt,
        eligible_gateway_list: Vec<RoutableConnectorChoice>,
        ranking_algorithm: Option<RankingAlgorithm>,
        is_elimination_enabled: bool,
    ) -> Self
    where
        Self: Sized;

    fn construct_debit_request(
        attempt: &PaymentAttempt,
        metadata: Option<String>,
        card_isin: Option<Secret<String>>,
        ranking_algorithm: Option<RankingAlgorithm>,
    ) -> Self
    where
        Self: Sized;
}

#[cfg(feature = "v1")]
impl OpenRouterDecideGatewayRequestExt for OpenRouterDecideGatewayRequest {
    fn construct_sr_request(
        attempt: &PaymentAttempt,
        eligible_gateway_list: Vec<RoutableConnectorChoice>,
        ranking_algorithm: Option<RankingAlgorithm>,
        is_elimination_enabled: bool,
    ) -> Self {
        Self {
            payment_info: PaymentInfo {
                payment_id: attempt.payment_id.clone(),
                amount: attempt.net_amount.get_order_amount(),
                currency: attempt.currency.unwrap_or(storage_enums::Currency::USD),
                payment_type: "ORDER_PAYMENT".to_string(),
                payment_method_type: "UPI".into(), // TODO: once open-router makes this field string, we can send from attempt
                payment_method: attempt.payment_method.unwrap_or_default(),
                metadata: None,
                card_isin: None,
            },
            merchant_id: attempt.profile_id.clone(),
            eligible_gateway_list: Some(
                eligible_gateway_list
                    .into_iter()
                    .map(|connector| connector.to_string())
                    .collect(),
            ),
            ranking_algorithm,
            elimination_enabled: Some(is_elimination_enabled),
        }
    }

    fn construct_debit_request(
        attempt: &PaymentAttempt,
        metadata: Option<String>,
        card_isin: Option<Secret<String>>,
        ranking_algorithm: Option<RankingAlgorithm>,
    ) -> Self {
        Self {
            payment_info: PaymentInfo {
                payment_id: attempt.payment_id.clone(),
                amount: attempt.net_amount.get_total_amount(),
                currency: attempt.currency.unwrap_or(storage_enums::Currency::USD),
                payment_type: "ORDER_PAYMENT".to_string(),
                card_isin: card_isin.map(|value| value.peek().clone()),
                metadata,
                payment_method_type: "UPI".into(), // TODO: once open-router makes this field string, we can send from attempt
                payment_method: attempt.payment_method.unwrap_or_default(),
            },
            merchant_id: attempt.profile_id.clone(),
            // eligible gateway list is not used in debit routing
            eligible_gateway_list: None,
            ranking_algorithm,
            elimination_enabled: None,
        }
    }
}


    pub merchant_id: Secret<String>,
    #[serde(rename = "RefID")]
    pub ref_id: String,
    #[serde(rename = "RefundID")]
    pub refund_id: String,
    #[serde(rename = "TxnID")]
    pub txn_id: String,
    pub amount: StringMajorUnit,
    pub status: FiuuRefundsWebhookStatus,
    pub signature: Secret<String>,
}

#[derive(Debug, Deserialize, Serialize, Clone, strum::Display)]
pub enum FiuuRefundsWebhookStatus {
    #[strum(serialize = "00")]
    #[serde(rename = "00")]
    RefundSuccess,
    #[strum(serialize = "11")]
    #[serde(rename = "11")]
    RefundFailure,
    #[strum(serialize = "22")]
    #[serde(rename = "22")]
    RefundPending,
}

#[derive(Debug, Deserialize, Serialize, Clone, strum::Display)]
pub enum FiuuWebhooksRefundType {
    P,
}

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

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

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct FiuWebhookEvent {
    pub status: FiuuPaymentWebhookStatus,
}

#[derive(Debug, Deserialize, Serialize, Clone, strum::Display)]
pub enum FiuuPaymentWebhookStatus {
    #[strum(serialize = "00")]
    #[serde(rename = "00")]
    Success,
    #[strum(serialize = "11")]
    #[serde(rename = "11")]
    Failure,
    #[strum(serialize = "22")]
    #[serde(rename = "22")]
    Pending,
}

impl From<FiuuPaymentWebhookStatus> for StatCode {
    fn from(value: FiuuPaymentWebhookStatus) -> Self {
        match value {
            FiuuPaymentWebhookStatus::Success => Self::Success,
            FiuuPaymentWebhookStatus::Failure => Self::Failure,
            FiuuPaymentWebhookStatus::Pending => Self::Pending,
        }
    }
}

impl From<FiuuPaymentWebhookStatus> for api_models::webhooks::IncomingWebhookEvent {
    fn from(value: FiuuPaymentWebhookStatus) -> Self {
        match value {
            FiuuPaymentWebhookStatus::Success => Self::PaymentIntentSuccess,
            FiuuPaymentWebhookStatus::Failure => Self::PaymentIntentFailure,
            FiuuPaymentWebhookStatus::Pending => Self::PaymentIntentProcessing,
        }
    }
}

impl From<FiuuRefundsWebhookStatus> for api_models::webhooks::IncomingWebhookEvent {
    fn from(value: FiuuRefundsWebhookStatus) -> Self {
        match value {
            FiuuRefundsWebhookStatus::RefundSuccess => Self::RefundSuccess,
            FiuuRefundsWebhookStatus::RefundFailure => Self::RefundFailure,
            FiuuRefundsWebhookStatus::RefundPending => Self::EventNotSupported,
        }
    }
}

impl From<FiuuRefundsWebhookStatus> for enums::RefundStatus {
    fn from(value: FiuuRefundsWebhookStatus) -> Self {
        match value {
            FiuuRefundsWebhookStatus::RefundFailure => Self::Failure,
            FiuuRefundsWebhookStatus::RefundSuccess => Self::Success,
            FiuuRefundsWebhookStatus::RefundPending => Self::Pending,
        }
    }
}


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

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

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

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

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

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

static CONNECTOR: PaytmTest = PaytmTest {};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Connector dependent test cases goes here

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


pub async fn refunds_list_profile(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<api_models::refunds::RefundListRequest>,
) -> HttpResponse {
    let flow = Flow::RefundsList;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload.into_inner(),
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            refund_list(
                state,
                merchant_context,
                auth.profile_id.map(|profile_id| vec![profile_id]),
                req,
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::ProfileRefundRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct DeutschebankThreeDSInitializeRequestCustomerBillingData {
    street: Secret<String>,
    postal_code: Secret<String>,
    city: String,
    state: Secret<String>,
    country: String,
}


pub struct ApplepayMerchantResponse {
    pub status_message: String,
}


pub fn should_perform_elimination_routing(self) -> bool {
        match self {
            Self::ProcessorDowntime | Self::ProcessorDeclineUnauthorized => true,
            Self::IssueWithPaymentMethod
            | Self::ProcessorDeclineIncorrectData
            | Self::FrmDecline
            | Self::HardDecline
            | Self::SoftDecline => false,
        }
    }


pub struct BarclaycardServerErrorResponse {
    pub status: Option<String>,
    pub message: Option<String>,
    pub reason: Option<Reason>,
}


OpenAPI Block Path: paths."/customers"
{
  "post": {
    "tags": [
      "Customers"
    ],
    "summary": "Customers - Create",
    "description": "Creates a customer object and stores the customer details to be reused for future payments.\nIncase the customer already exists in the system, this API will respond with the customer details.",
    "operationId": "Create a Customer",
    "requestBody": {
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CustomerRequest"
          },
          "examples": {
            "Update name and email of a customer": {
              "value": {
                "email": "guest@example.com",
                "name": "John Doe"
              }
            }
          }
        }
      },
      "required": true
    },
    "responses": {
      "200": {
        "description": "Customer Created",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CustomerResponse"
            }
          }
        }
      },
      "400": {
        "description": "Invalid data"
      }
    },
    "security": [
      {
        "api_key": []
      }
    ]
  }
}


pub struct NovalnetRouterData<T> {
    pub amount: StringMinorUnit,
    pub router_data: T,
}


impl api::PaymentCapture for Cashtocode {}

impl api::PaymentCapture for for Cashtocode