// File: crates/router/src/core/apple_pay_certificates_migration.rs
// Module: router
// Public functions: 1

use api_models::apple_pay_certificates_migration;
use common_utils::{errors::CustomResult, type_name, types::keymanager::Identifier};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};

use super::{
    errors::{self, StorageErrorExt},
    payments::helpers,
};
use crate::{
    routes::SessionState,
    services::{self, logger},
    types::{domain::types as domain_types, storage},
};

#[cfg(feature = "v1")]
pub async fn apple_pay_certificates_migration(
    state: SessionState,
    req: &apple_pay_certificates_migration::ApplePayCertificatesMigrationRequest,
) -> CustomResult<
    services::ApplicationResponse<
        apple_pay_certificates_migration::ApplePayCertificatesMigrationResponse,
    >,
    errors::ApiErrorResponse,
> {
    let db = state.store.as_ref();

    let merchant_id_list = &req.merchant_ids;

    let mut migration_successful_merchant_ids = vec![];
    let mut migration_failed_merchant_ids = vec![];
    let key_manager_state = &(&state).into();
    for merchant_id in merchant_id_list {
        let key_store = state
            .store
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                merchant_id,
                &state.store.get_master_key().to_vec().into(),
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)?;

        let merchant_connector_accounts = db
            .find_merchant_connector_account_by_merchant_id_and_disabled_list(
                key_manager_state,
                merchant_id,
                true,
                &key_store,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?;

        let mut mca_to_update = vec![];

        for connector_account in merchant_connector_accounts {
            let connector_apple_pay_metadata =
                helpers::get_applepay_metadata(connector_account.clone().metadata)
                    .map_err(|error| {
                        logger::error!(
                "Apple pay metadata parsing failed for {:?} in certificates migrations api {:?}",
                connector_account.clone().connector_name,
                error
            )
                    })
                    .ok();
            if let Some(apple_pay_metadata) = connector_apple_pay_metadata {
                let encrypted_apple_pay_metadata = domain_types::crypto_operation(
                    &(&state).into(),
                    type_name!(storage::MerchantConnectorAccount),
                    domain_types::CryptoOperation::Encrypt(Secret::new(
                        serde_json::to_value(apple_pay_metadata)
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Failed to serialize apple pay metadata as JSON")?,
                    )),
                    Identifier::Merchant(merchant_id.clone()),
                    key_store.key.get_inner().peek(),
                )
                .await
                .and_then(|val| val.try_into_operation())
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to encrypt connector apple pay metadata")?;

                let updated_mca =
                    storage::MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate {
                        connector_wallets_details: encrypted_apple_pay_metadata,
                    };

                mca_to_update.push((connector_account, updated_mca.into()));
            }
        }

        let merchant_connector_accounts_update = db
            .update_multiple_merchant_connector_accounts(mca_to_update)
            .await;

        match merchant_connector_accounts_update {
            Ok(_) => {
                logger::debug!(
                    "Merchant connector accounts updated for merchant id {merchant_id:?}"
                );
                migration_successful_merchant_ids.push(merchant_id.clone());
            }
            Err(error) => {
                logger::debug!(
                    "Merchant connector accounts update failed with error {error} for merchant id {merchant_id:?}");
                migration_failed_merchant_ids.push(merchant_id.clone());
            }
        };
    }

    Ok(services::api::ApplicationResponse::Json(
        apple_pay_certificates_migration::ApplePayCertificatesMigrationResponse {
            migration_successful: migration_successful_merchant_ids,
            migration_failed: migration_failed_merchant_ids,
        },
    ))
}


// Implementation: impl api::MandateSetup for for Inespay
// File: crates/hyperswitch_connectors/src/connectors/inespay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::MandateSetup for for Inespay


// Implementation: impl ConnectorCommon for for Prophetpay
// File: crates/hyperswitch_connectors/src/connectors/prophetpay.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl ConnectorCommon for for Prophetpay


// Function: decide_payout_connector
// File: crates/router/src/core/payouts/helpers.rs
// Module: router

pub fn decide_payout_connector(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    request_straight_through: Option<api::routing::StraightThroughAlgorithm>,
    routing_data: &mut storage::RoutingData,
    payout_data: &mut PayoutData,
    eligible_connectors: Option<Vec<enums::RoutableConnectors>>,
) -> RouterResult<api::ConnectorCallType>


// File: crates/euclid/src/frontend.rs
// Module: euclid

pub mod ast;
pub mod dir;
pub mod vir;


// Implementation: impl OpenSearchAuth
// File: crates/analytics/src/opensearch.rs
// Module: analytics
// Methods: 1 total (1 public)

impl OpenSearchAuth


// Struct: DeliveryObject
// File: crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DeliveryObject


// Struct: ClientSecretCreateRequest
// File: crates/api_models/src/ephemeral_key.rs
// Module: api_models
// Implementations: 1
// Traits: common_utils::events::ApiEventMetric

pub struct ClientSecretCreateRequest


// Implementation: impl crate::events::ApiEventMetric for for GlobalTokenId
// File: crates/common_utils/src/id_type/global_id/token.rs
// Module: common_utils
// Methods: 1 total (0 public)

impl crate::events::ApiEventMetric for for GlobalTokenId


// Implementation: impl ConnectorValidation for for Globalpay
// File: crates/hyperswitch_connectors/src/connectors/globalpay.rs
// Module: hyperswitch_connectors
// Methods: 1 total (0 public)

impl ConnectorValidation for for Globalpay


// Struct: MerchantConnectorListResponse
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 2

pub struct MerchantConnectorListResponse


// Struct: FiuWebhookEvent
// File: crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FiuWebhookEvent


// Function: perform_decision_management
// File: crates/router/src/core/payments/conditional_configs.rs
// Module: router

pub fn perform_decision_management(
    state: &routes::SessionState,
    algorithm_ref: routing::RoutingAlgorithmRef,
    merchant_id: &common_utils::id_type::MerchantId,
    payment_data: &core_routing::PaymentsDslInput<'_>,
) -> ConditionalConfigResult<common_types::payments::ConditionalConfigs>


// Implementation: impl UnifiedAuthenticationService
// File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service.rs
// Module: hyperswitch_connectors
// Methods: 1 total (1 public)

impl UnifiedAuthenticationService


// File: crates/scheduler/src/settings.rs
// Module: scheduler
// Public functions: 2
// Public structs: 3

use common_utils::ext_traits::ConfigExt;
use serde::Deserialize;
use storage_impl::errors::ApplicationError;

pub use crate::configs::settings::SchedulerSettings;

#[derive(Debug, Clone, Deserialize)]
pub struct ProducerSettings {
    pub upper_fetch_limit: i64,
    pub lower_fetch_limit: i64,

    pub lock_key: String,
    pub lock_ttl: i64,
    pub batch_size: usize,
}

#[derive(Debug, Clone, Deserialize)]
pub struct ConsumerSettings {
    pub disabled: bool,
    pub consumer_group: String,
}

#[cfg(feature = "kv_store")]
#[derive(Debug, Clone, Deserialize)]
pub struct DrainerSettings {
    pub stream_name: String,
    pub num_partitions: u8,
    pub max_read_count: u64,
    pub shutdown_interval: u32, // in milliseconds
    pub loop_interval: u32,     // in milliseconds
}

impl ProducerSettings {
    pub fn validate(&self) -> Result<(), ApplicationError> {
        common_utils::fp_utils::when(self.lock_key.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "producer lock key must not be empty".into(),
            ))
        })
    }
}

#[cfg(feature = "kv_store")]
impl DrainerSettings {
    pub fn validate(&self) -> Result<(), ApplicationError> {
        common_utils::fp_utils::when(self.stream_name.is_default_or_empty(), || {
            Err(ApplicationError::InvalidConfigurationValueError(
                "drainer stream name must not be empty".into(),
            ))
        })
    }
}

impl Default for ProducerSettings {
    fn default() -> Self {
        Self {
            upper_fetch_limit: 0,
            lower_fetch_limit: 1800,
            lock_key: "PRODUCER_LOCKING_KEY".into(),
            lock_ttl: 160,
            batch_size: 200,
        }
    }
}

impl Default for ConsumerSettings {
    fn default() -> Self {
        Self {
            disabled: false,
            consumer_group: "SCHEDULER_GROUP".into(),
        }
    }
}

#[cfg(feature = "kv_store")]
impl Default for DrainerSettings {
    fn default() -> Self {
        Self {
            stream_name: "DRAINER_STREAM".into(),
            num_partitions: 64,
            max_read_count: 100,
            shutdown_interval: 1000,
            loop_interval: 500,
        }
    }
}


// Struct: QrTxnData
// File: crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct QrTxnData


#[async_trait::async_trait]
impl IncomingWebhook for Worldpay {
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::Sha256))
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let mut event_signature =
            get_header_key_value("Event-Signature", request.headers)?.split(',');
        let sign_header = event_signature
            .next_back()
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)?;
        let signature = sign_header
            .split('/')
            .next_back()
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)?;
        hex::decode(signature).change_context(errors::ConnectorError::WebhookResponseEncodingFailed)
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        Ok(request.body.to_vec())
    }

    async fn verify_webhook_source(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: crypto::Encryptable<masking::Secret<serde_json::Value>>,
        connector_label: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let connector_webhook_secrets = self
            .get_webhook_source_verification_merchant_secret(
                merchant_id,
                connector_label,
                connector_webhook_details,
            )
            .await
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
        let signature = self
            .get_webhook_source_verification_signature(request, &connector_webhook_secrets)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
        let message = self
            .get_webhook_source_verification_message(
                request,
                merchant_id,
                &connector_webhook_secrets,
            )
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
        let secret_key = hex::decode(connector_webhook_secrets.secret)
            .change_context(errors::ConnectorError::WebhookVerificationSecretInvalid)?;

        let signing_key = hmac::Key::new(hmac::HMAC_SHA256, &secret_key);
        let signed_message = hmac::sign(&signing_key, &message);
        let computed_signature = hex::encode(signed_message.as_ref());

        Ok(computed_signature.as_bytes() == hex::encode(signature).as_bytes())
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let body: WorldpayWebhookTransactionId = request
            .body
            .parse_struct("WorldpayWebhookTransactionId")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
            PaymentIdType::PaymentAttemptId(body.event_details.transaction_reference),
        ))
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        let body: WorldpayWebhookEventType = request
            .body
            .parse_struct("WorldpayWebhookEventType")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        match body.event_details.event_type {
            EventType::Authorized => Ok(IncomingWebhookEvent::PaymentIntentAuthorizationSuccess),
            EventType::Settled => Ok(IncomingWebhookEvent::PaymentIntentSuccess),
            EventType::SentForSettlement | EventType::SentForAuthorization => {
                Ok(IncomingWebhookEvent::PaymentIntentProcessing)
            }
            EventType::Error | EventType::Expired | EventType::SettlementFailed => {
                Ok(IncomingWebhookEvent::PaymentIntentFailure)
            }
            EventType::Unknown
            | EventType::Cancelled
            | EventType::Refused
            | EventType::Refunded
            | EventType::SentForRefund
            | EventType::RefundFailed => Ok(IncomingWebhookEvent::EventNotSupported),
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let body: WorldpayWebhookEventType = request
            .body
            .parse_struct("WorldpayWebhookEventType")
            .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
        let psync_body = WorldpayEventResponse::try_from(body)?;
        Ok(Box::new(psync_body))
    }

    fn get_mandate_details(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorMandateDetails>,
        errors::ConnectorError,
    > {
        let body: WorldpayWebhookTransactionId = request
            .body
            .parse_struct("WorldpayWebhookTransactionId")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        let mandate_reference = body.event_details.token.map(|mandate_token| {
            hyperswitch_domain_models::router_flow_types::ConnectorMandateDetails {
                connector_mandate_id: mandate_token.href,
            }
        });
        Ok(mandate_reference)
    }

    fn get_network_txn_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<hyperswitch_domain_models::router_flow_types::ConnectorNetworkTxnId>,
        errors::ConnectorError,
    > {
        let body: WorldpayWebhookTransactionId = request
            .body
            .parse_struct("WorldpayWebhookTransactionId")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        let optional_network_txn_id = body.event_details.scheme_reference.map(|network_txn_id| {
            hyperswitch_domain_models::router_flow_types::ConnectorNetworkTxnId::new(network_txn_id)
        });
        Ok(optional_network_txn_id)
    }
}

impl ConnectorRedirectResponse for Worldpay {
    fn get_flow_type(
        &self,
        _query_params: &str,
        _json_payload: Option<serde_json::Value>,
        action: PaymentAction,
    ) -> CustomResult<enums::CallConnectorAction, errors::ConnectorError> {
        match action {
            PaymentAction::CompleteAuthorize => Ok(enums::CallConnectorAction::Trigger),
            PaymentAction::PSync | PaymentAction::PaymentAuthenticateCompleteAuthorize => {
                Ok(enums::CallConnectorAction::Avoid)
            }
        }
    }
}

static WORLDPAY_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(|| {
        let supported_capture_methods = vec![
            enums::CaptureMethod::Automatic,
            enums::CaptureMethod::Manual,
            enums::CaptureMethod::SequentialAutomatic,
        ];

        let supported_card_network = vec![
            common_enums::CardNetwork::AmericanExpress,
            common_enums::CardNetwork::CartesBancaires,
            common_enums::CardNetwork::DinersClub,
            common_enums::CardNetwork::JCB,
            common_enums::CardNetwork::Maestro,
            common_enums::CardNetwork::Mastercard,
            common_enums::CardNetwork::Visa,
        ];

        let mut worldpay_supported_payment_methods = SupportedPaymentMethods::new();

        worldpay_supported_payment_methods.add(
            enums::PaymentMethod::Card,
            enums::PaymentMethodType::Credit,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::Supported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                ),
            },
        );

        worldpay_supported_payment_methods.add(
            enums::PaymentMethod::Card,
            enums::PaymentMethodType::Debit,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::Supported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: Some(
                    api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                        api_models::feature_matrix::CardSpecificFeatures {
                            three_ds: common_enums::FeatureStatus::Supported,
                            no_three_ds: common_enums::FeatureStatus::Supported,
                            supported_card_networks: supported_card_network.clone(),
                        }
                    }),
                ),
            },
        );

        worldpay_supported_payment_methods.add(
            enums::PaymentMethod::Wallet,
            enums::PaymentMethodType::GooglePay,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        worldpay_supported_payment_methods.add(
            enums::PaymentMethod::Wallet,
            enums::PaymentMethodType::ApplePay,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        worldpay_supported_payment_methods
    });

static WORLDPAY_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Worldpay",
    description: "Worldpay is a payment gateway and PSP enabling secure online transactions",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Live,
};

static WORLDPAY_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 1] = [enums::EventClass::Payments];

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

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

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


// Struct: AirwallexPaymentsResponse
// File: crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AirwallexPaymentsResponse


// File: crates/router_derive/src/macros/schema.rs
// Module: router_derive
// Public functions: 2

mod helpers;

use quote::quote;

use crate::macros::{
    helpers as macro_helpers,
    schema::helpers::{HasSchemaParameters, IsSchemaFieldApplicableForValidation},
};

pub fn validate_schema_derive(input: syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
    let name = &input.ident;

    // Extract struct fields
    let fields = macro_helpers::get_struct_fields(input.data)
        .map_err(|error| syn::Error::new(proc_macro2::Span::call_site(), error))?;

    // Map over each field
    let validation_checks = fields.iter().filter_map(|field| {
        let field_name = field.ident.as_ref()?;
        let field_type = &field.ty;

        // Check if field type is valid for validation
        let is_field_valid = match IsSchemaFieldApplicableForValidation::from(field_type) {
            IsSchemaFieldApplicableForValidation::Invalid => return None,
            val => val,
        };

        // Parse attribute parameters for 'schema'
        let schema_params = match field.get_schema_parameters() {
            Ok(params) => params,
            Err(_) => return None,
        };

        let min_length = schema_params.min_length;
        let max_length = schema_params.max_length;

        // Skip if no length validation is needed
        if min_length.is_none() && max_length.is_none() {
            return None;
        }

        let min_check = min_length.map(|min_val| {
            quote! {
                if value_len < #min_val {
                    return Err(format!("{} must be at least {} characters long. Received {} characters",
                        stringify!(#field_name), #min_val, value_len));
                }
            }
        }).unwrap_or_else(|| quote! {});

        let max_check = max_length.map(|max_val| {
            quote! {
                if value_len > #max_val {
                    return Err(format!("{} must be at most {} characters long. Received {} characters",
                        stringify!(#field_name), #max_val, value_len));
                }
            }
        }).unwrap_or_else(|| quote! {});

        // Generate length validation
        if is_field_valid == IsSchemaFieldApplicableForValidation::ValidOptional {
            Some(quote! {
                if let Some(value) = &self.#field_name {
                    let value_len = value.as_str().len();
                    #min_check
                    #max_check
                }
            })
        } else {
            Some(quote! {
                {
                    let value_len = self.#field_name.as_str().len();
                    #min_check
                    #max_check
                }
            })
        }
    }).collect::<Vec<_>>();

    Ok(quote! {
        impl #name {
            pub fn validate(&self) -> Result<(), String> {
                #(#validation_checks)*
                Ok(())
            }
        }
    })
}


// Function: get_flow
// File: crates/router/src/types/domain/user/decision_manager.rs
// Module: router

pub fn get_flow(&self) -> UserFlow


// Struct: MerchantAccountResponse
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 0

pub struct MerchantAccountResponse


// Struct: StreamData
// File: crates/drainer/src/types.rs
// Module: drainer
// Implementations: 1

pub struct StreamData


// Struct: GlobepayAuthType
// File: crates/hyperswitch_connectors/src/connectors/globepay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GlobepayAuthType


// Struct: NuveiPayoutRequest
// File: crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NuveiPayoutRequest


// Function: modify_trackers
// File: crates/router/src/core/payments/retry.rs
// Module: router

pub fn modify_trackers<F, FData, D>(
    state: &routes::SessionState,
    connector: String,
    payment_data: &mut D,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: storage_enums::MerchantStorageScheme,
    router_data: types::RouterData<F, FData, types::PaymentsResponseData>,
    is_step_up: bool,
) -> RouterResult<()>
where
    F: Clone + Send,
    FData: Send,
    D: payments::OperationSessionGetters<F> + payments::OperationSessionSetters<F> + Send + Sync,


// Function: set_request_body
// File: crates/router/src/core/payments/routing/utils.rs
// Module: router

pub fn set_request_body(&mut self, request: Req)


// Implementation: impl api::PaymentSession for for Bluesnap
// File: crates/hyperswitch_connectors/src/connectors/bluesnap.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSession for for Bluesnap


// Function: add_browser_data
// File: crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs
// Module: hyperswitch_connectors

pub fn add_browser_data(mut self, browser_info: BrowserInformation) -> Result<Self, Error>


// File: crates/router/src/db/payment_link.rs
// Module: router

use error_stack::report;
use router_env::{instrument, tracing};

use crate::{
    connection,
    core::errors::{self, CustomResult},
    db::MockDb,
    services::Store,
    types::storage::{self, PaymentLinkDbExt},
};

#[async_trait::async_trait]
pub trait PaymentLinkInterface {
    async fn find_payment_link_by_payment_link_id(
        &self,
        payment_link_id: &str,
    ) -> CustomResult<storage::PaymentLink, errors::StorageError>;

    async fn insert_payment_link(
        &self,
        _payment_link: storage::PaymentLinkNew,
    ) -> CustomResult<storage::PaymentLink, errors::StorageError>;

    async fn list_payment_link_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_link_constraints: api_models::payments::PaymentLinkListConstraints,
    ) -> CustomResult<Vec<storage::PaymentLink>, errors::StorageError>;
}

#[async_trait::async_trait]
impl PaymentLinkInterface for Store {
    #[instrument(skip_all)]
    async fn find_payment_link_by_payment_link_id(
        &self,
        payment_link_id: &str,
    ) -> CustomResult<storage::PaymentLink, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::PaymentLink::find_link_by_payment_link_id(&conn, payment_link_id)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn insert_payment_link(
        &self,
        payment_link_config: storage::PaymentLinkNew,
    ) -> CustomResult<storage::PaymentLink, errors::StorageError> {
        let conn = connection::pg_connection_write(self).await?;
        payment_link_config
            .insert(&conn)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }

    #[instrument(skip_all)]
    async fn list_payment_link_by_merchant_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_link_constraints: api_models::payments::PaymentLinkListConstraints,
    ) -> CustomResult<Vec<storage::PaymentLink>, errors::StorageError> {
        let conn = connection::pg_connection_read(self).await?;
        storage::PaymentLink::filter_by_constraints(&conn, merchant_id, payment_link_constraints)
            .await
            .map_err(|error| report!(errors::StorageError::from(error)))
    }
}

#[async_trait::async_trait]
impl PaymentLinkInterface for MockDb {
    async fn insert_payment_link(
        &self,
        _payment_link: storage::PaymentLinkNew,
    ) -> CustomResult<storage::PaymentLink, errors::StorageError> {
        // TODO: Implement function for `MockDb`
        Err(errors::StorageError::MockDbError)?
    }

    async fn find_payment_link_by_payment_link_id(
        &self,
        _payment_link_id: &str,
    ) -> CustomResult<storage::PaymentLink, errors::StorageError> {
        // TODO: Implement function for `MockDb`x
        Err(errors::StorageError::MockDbError)?
    }

    async fn list_payment_link_by_merchant_id(
        &self,
        _merchant_id: &common_utils::id_type::MerchantId,
        _payment_link_constraints: api_models::payments::PaymentLinkListConstraints,
    ) -> CustomResult<Vec<storage::PaymentLink>, errors::StorageError> {
        // TODO: Implement function for `MockDb`x
        Err(errors::StorageError::MockDbError)?
    }
}


// Function: create_new_merchant_and_insert_in_db
// File: crates/router/src/types/domain/user.rs
// Module: router

pub fn create_new_merchant_and_insert_in_db(
        &self,
        state: SessionState,
    ) -> UserResult<domain::MerchantAccount>


// Struct: Ebanx
// File: crates/hyperswitch_connectors/src/connectors/ebanx.rs
// Module: hyperswitch_connectors
// Implementations: 24
// Traits: api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, api::Payouts, api::PayoutCancel, api::PayoutCreate, api::PayoutEligibility, api::PayoutQuote, api::PayoutRecipient, api::PayoutFulfill, ConnectorCommon, ConnectorValidation, IncomingWebhook, ConnectorSpecifications

pub struct Ebanx

api::Payment api::PaymentSession api::ConnectorAccessToken api::MandateSetup api::PaymentAuthorize api::PaymentSync api::PaymentCapture api::PaymentVoid api::Refund api::RefundExecute api::RefundSync api::PaymentToken api::Payouts api::PayoutCancel api::PayoutCreate api::PayoutEligibility api::PayoutQuote api::PayoutRecipient api::PayoutFulfill ConnectorCommon ConnectorValidation IncomingWebhook ConnectorSpecifications


// Struct: CoBadgedCardNetworksInfo
// File: crates/api_models/src/open_router.rs
// Module: api_models
// Implementations: 0

pub struct CoBadgedCardNetworksInfo


            refund_id.as_str(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::RefundNotFound)?;

    Box::pin(refund_retrieve_core(
        state.clone(),
        merchant_context,
        profile_id,
        request,
        refund,
    ))
    .await
}

#[instrument(skip_all)]
#[cfg(feature = "olap")]
pub async fn refund_manual_update(
    state: SessionState,
    req: api_models::refunds::RefundManualUpdateRequest,
) -> RouterResponse<serde_json::Value> {
    let key_manager_state = &(&state).into();
    let key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &req.merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
        .attach_printable("Error while fetching the key store by merchant_id")?;
    let merchant_account = state
        .store
        .find_merchant_account_by_merchant_id(key_manager_state, &req.merchant_id, &key_store)
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
        .attach_printable("Error while fetching the merchant_account by merchant_id")?;
    let refund = state
        .store
        .find_refund_by_merchant_id_refund_id(
            merchant_account.get_id(),
            &req.refund_id,
            merchant_account.storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::RefundNotFound)?;
    let refund_update = diesel_refund::RefundUpdate::ManualUpdate {
        refund_status: req.status.map(common_enums::RefundStatus::from),
        refund_error_message: req.error_message,
        refund_error_code: req.error_code,
        updated_by: merchant_account.storage_scheme.to_string(),
    };
    state
        .store
        .update_refund(
            refund.to_owned(),
            refund_update,
            merchant_account.storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!(
                "Failed while updating refund: refund_id: {}",
                refund.refund_id
            )
        })?;
    Ok(services::ApplicationResponse::StatusOk)
}

#[instrument(skip_all)]
#[cfg(feature = "olap")]
pub async fn get_filters_for_refunds(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
) -> RouterResponse<api_models::refunds::RefundListFilters> {
    let merchant_connector_accounts = if let services::ApplicationResponse::Json(data) =
        super::admin::list_payment_connectors(
            state,
            merchant_context.get_merchant_account().get_id().to_owned(),
            profile_id_list,
        )
        .await?
    {
        data
    } else {
        return Err(errors::ApiErrorResponse::InternalServerError.into());
    };

    let connector_map = merchant_connector_accounts
        .into_iter()
        .filter_map(|merchant_connector_account| {
            merchant_connector_account
                .connector_label
                .clone()
                .map(|label| {
                    let info = merchant_connector_account.to_merchant_connector_info(&label);
                    (merchant_connector_account.connector_name, info)
                })
        })
        .fold(
            HashMap::new(),
            |mut map: HashMap<String, Vec<MerchantConnectorInfo>>, (connector_name, info)| {
                map.entry(connector_name).or_default().push(info);
                map
            },
        );

    Ok(services::ApplicationResponse::Json(
        api_models::refunds::RefundListFilters {
            connector: connector_map,
            currency: enums::Currency::iter().collect(),
            refund_status: enums::RefundStatus::iter().collect(),
        },
    ))
}

#[instrument(skip_all)]
#[cfg(feature = "olap")]
pub async fn get_aggregates_for_refunds(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
    time_range: common_utils::types::TimeRange,
) -> RouterResponse<api_models::refunds::RefundAggregateResponse> {
    let db = state.store.as_ref();
    let refund_status_with_count = db
        .get_refund_status_with_count(
            merchant_context.get_merchant_account().get_id(),
            profile_id_list,
            &time_range,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to find status count")?;
    let mut status_map: HashMap<enums::RefundStatus, i64> =
        refund_status_with_count.into_iter().collect();
    for status in enums::RefundStatus::iter() {
        status_map.entry(status).or_default();
    }

    Ok(services::ApplicationResponse::Json(
        api_models::refunds::RefundAggregateResponse {
            status_with_count: status_map,
        },
    ))
}

impl ForeignFrom<diesel_refund::Refund> for api::RefundResponse {
    fn foreign_from(refund: diesel_refund::Refund) -> Self {
        let refund = refund;

        Self {
            payment_id: refund.payment_id,
            refund_id: refund.refund_id,
            amount: refund.refund_amount,
            currency: refund.currency.to_string(),
            reason: refund.refund_reason,
            status: refund.refund_status.foreign_into(),
            profile_id: refund.profile_id,
            metadata: refund.metadata,
            error_message: refund.refund_error_message,
            error_code: refund.refund_error_code,
            created_at: Some(refund.created_at),
            updated_at: Some(refund.modified_at),
            connector: refund.connector,
            merchant_connector_id: refund.merchant_connector_id,
            split_refunds: refund.split_refunds,
            unified_code: refund.unified_code,
            unified_message: refund.unified_message,
            issuer_error_code: refund.issuer_error_code,
            issuer_error_message: refund.issuer_error_message,
        }
    }
}

// ********************************************** PROCESS TRACKER **********************************************

#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn schedule_refund_execution(
    state: &SessionState,
    refund: diesel_refund::Refund,
    refund_type: api_models::refunds::RefundType,
    merchant_context: &domain::MerchantContext,
    payment_attempt: &storage::PaymentAttempt,
    payment_intent: &storage::PaymentIntent,
    creds_identifier: Option<String>,
    split_refunds: Option<SplitRefundsRequest>,
) -> RouterResult<diesel_refund::Refund> {
    // refunds::RefundResponse> {
    let db = &*state.store;
    let runner = storage::ProcessTrackerRunner::RefundWorkflowRouter;
    let task = "EXECUTE_REFUND";
    let task_id = format!("{runner}_{task}_{}", refund.internal_reference_id);

    let refund_process = db
        .find_process_by_id(&task_id)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to find the process id")?;

    let result = match refund.refund_status {
        enums::RefundStatus::Pending | enums::RefundStatus::ManualReview => {
            match (refund.sent_to_gateway, refund_process) {
                (false, None) => {
                    // Execute the refund task based on refund_type
                    match refund_type {
                        api_models::refunds::RefundType::Scheduled => {
                            add_refund_execute_task(db, &refund, runner)
                                .await
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable_lazy(|| format!("Failed while pushing refund execute task to scheduler, refund_id: {}", refund.refund_id))?;

                            Ok(refund)
                        }
                        api_models::refunds::RefundType::Instant => {
                            let update_refund = Box::pin(trigger_refund_to_gateway(
                                state,
                                &refund,
                                merchant_context,
                                payment_attempt,
                                payment_intent,
                                creds_identifier,
                                split_refunds,
                            ))
                            .await;

                            match update_refund {
                                Ok(updated_refund_data) => {
                                    add_refund_sync_task(db, &updated_refund_data, runner)
                                        .await
                                        .change_context(errors::ApiErrorResponse::InternalServerError)
                                        .attach_printable_lazy(|| format!(
                                            "Failed while pushing refund sync task in scheduler: refund_id: {}",
                                            refund.refund_id
                                        ))?;
                                    Ok(updated_refund_data)
                                }
                                Err(err) => Err(err),
                            }
                        }
                    }
                }
                _ => {
                    // Sync the refund for status check
                    //[#300]: return refund status response
                    match refund_type {
                        api_models::refunds::RefundType::Scheduled => {
                            add_refund_sync_task(db, &refund, runner)
                                .await
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable_lazy(|| format!("Failed while pushing refund sync task in scheduler: refund_id: {}", refund.refund_id))?;
                            Ok(refund)
                        }
                        api_models::refunds::RefundType::Instant => {
                            // [#255]: This is not possible in schedule_refund_execution as it will always be scheduled
                            // sync_refund_with_gateway(data, &refund).await
                            Ok(refund)
                        }
                    }
                }
            }
        }
        //  [#255]: This is not allowed to be otherwise or all
        _ => Ok(refund),
    }?;
    Ok(result)
}

#[instrument(skip_all)]
pub async fn sync_refund_with_gateway_workflow(
    state: &SessionState,
    refund_tracker: &storage::ProcessTracker,
) -> Result<(), errors::ProcessTrackerError> {
    let key_manager_state = &state.into();
    let refund_core = serde_json::from_value::<diesel_refund::RefundCoreWorkflow>(
        refund_tracker.tracking_data.clone(),
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable_lazy(|| {
        format!(
            "unable to convert into refund_core {:?}",
            refund_tracker.tracking_data
        )
    })?;

    let key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &refund_core.merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await?;

    let merchant_account = state
        .store
        .find_merchant_account_by_merchant_id(
            key_manager_state,
            &refund_core.merchant_id,
            &key_store,
        )
        .await?;
    let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
        merchant_account.clone(),
        key_store.clone(),
    )));
    let response = Box::pin(refund_retrieve_core_with_internal_reference_id(
        state.clone(),
        merchant_context,
        None,
        refund_core.refund_internal_reference_id,
        Some(true),
    ))
    .await?;
    let terminal_status = [
        enums::RefundStatus::Success,
        enums::RefundStatus::Failure,
        enums::RefundStatus::TransactionFailure,
    ];
    match response.refund_status {
        status if terminal_status.contains(&status) => {
            state
                .store
                .as_scheduler()
                .finish_process_with_business_status(
                    refund_tracker.clone(),
                    business_status::COMPLETED_BY_PT,
                )
                .await?
        }
        _ => {
            _ = retry_refund_sync_task(
                &*state.store,
                response.connector,
                response.merchant_id,
                refund_tracker.to_owned(),
            )
            .await?;
        }
    }

    Ok(())
}

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

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

#[instrument(skip_all)]
pub async fn start_refund_workflow(
    state: &SessionState,
    refund_tracker: &storage::ProcessTracker,
) -> Result<(), errors::ProcessTrackerError> {
    match refund_tracker.name.as_deref() {
        Some("EXECUTE_REFUND") => {
            Box::pin(trigger_refund_execute_workflow(state, refund_tracker)).await
        }
        Some("SYNC_REFUND") => {
            Box::pin(sync_refund_with_gateway_workflow(state, refund_tracker)).await
        }
        _ => Err(errors::ProcessTrackerError::JobNotFound),
    }
}

#[instrument(skip_all)]
pub async fn trigger_refund_execute_workflow(
    state: &SessionState,
    refund_tracker: &storage::ProcessTracker,
) -> Result<(), errors::ProcessTrackerError> {
    let db = &*state.store;
    let refund_core = serde_json::from_value::<diesel_refund::RefundCoreWorkflow>(
        refund_tracker.tracking_data.clone(),
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable_lazy(|| {
        format!(
            "unable to convert into refund_core {:?}",
            refund_tracker.tracking_data
        )
    })?;
    let key_manager_state = &state.into();
    let key_store = state
        .store
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &refund_core.merchant_id,
            &state.store.get_master_key().to_vec().into(),
        )
        .await?;

    let merchant_account = db
        .find_merchant_account_by_merchant_id(
            key_manager_state,
            &refund_core.merchant_id,
            &key_store,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
        merchant_account.clone(),
        key_store.clone(),
    )));
    let refund = db
        .find_refund_by_internal_reference_id_merchant_id(
            &refund_core.refund_internal_reference_id,
            &refund_core.merchant_id,
            merchant_account.storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::RefundNotFound)?;
    match (&refund.sent_to_gateway, &refund.refund_status) {
        (false, enums::RefundStatus::Pending) => {
            let merchant_account = db
                .find_merchant_account_by_merchant_id(
                    key_manager_state,
                    &refund.merchant_id,
                    &key_store,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

            let payment_attempt = db
                .find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
                    &refund.connector_transaction_id,
                    &refund_core.payment_id,
                    &refund.merchant_id,
                    merchant_account.storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

            let payment_intent = db
                .find_payment_intent_by_payment_id_merchant_id(
                    &(state.into()),
                    &payment_attempt.payment_id,
                    &refund.merchant_id,
                    &key_store,
                    merchant_account.storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
            let split_refunds = core_utils::get_split_refunds(SplitRefundInput {
                split_payment_request: payment_intent.split_payments.clone(),
                payment_charges: payment_attempt.charges.clone(),
                charge_id: payment_attempt.charge_id.clone(),
                refund_request: refund.split_refunds.clone(),
            })?;

            //trigger refund request to gateway
            let updated_refund = Box::pin(trigger_refund_to_gateway(
                state,
                &refund,
                &merchant_context,
                &payment_attempt,
                &payment_intent,
                None,
                split_refunds,
            ))
            .await?;
            add_refund_sync_task(
                db,
                &updated_refund,
                storage::ProcessTrackerRunner::RefundWorkflowRouter,
            )
            .await?;
        }
        (true, enums::RefundStatus::Pending) => {
            // create sync task
            add_refund_sync_task(
                db,
                &refund,
                storage::ProcessTrackerRunner::RefundWorkflowRouter,
            )
            .await?;
        }
        (_, _) => {
            //mark task as finished
            db.as_scheduler()
                .finish_process_with_business_status(
                    refund_tracker.clone(),
                    business_status::COMPLETED_BY_PT,
                )
                .await?;
        }
    };
    Ok(())
}

#[instrument]
pub fn refund_to_refund_core_workflow_model(
    refund: &diesel_refund::Refund,
) -> diesel_refund::RefundCoreWorkflow {
    diesel_refund::RefundCoreWorkflow {
        refund_internal_reference_id: refund.internal_reference_id.clone(),
        connector_transaction_id: refund.connector_transaction_id.clone(),
        merchant_id: refund.merchant_id.clone(),
        payment_id: refund.payment_id.clone(),
        processor_transaction_data: refund.processor_transaction_data.clone(),
    }
}

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

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

    Ok(response)
}

#[instrument(skip_all)]
pub async fn add_refund_execute_task(
    db: &dyn db::StorageInterface,
    refund: &diesel_refund::Refund,
    runner: storage::ProcessTrackerRunner,
) -> RouterResult<storage::ProcessTracker> {
    let task = "EXECUTE_REFUND";
    let process_tracker_id = format!("{runner}_{task}_{}", refund.internal_reference_id);
    let tag = ["REFUND"];
    let schedule_time = common_utils::date_time::now();
    let refund_workflow_tracking_data = refund_to_refund_core_workflow_model(refund);
    let process_tracker_entry = storage::ProcessTrackerNew::new(
        process_tracker_id,
        task,
        runner,
        tag,
        refund_workflow_tracking_data,
        None,
        schedule_time,
        common_types::consts::API_VERSION,
    )
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to construct refund execute process tracker task")?;

    let response = db
        .insert_process(process_tracker_entry)
        .await
        .to_duplicate_response(errors::ApiErrorResponse::DuplicateRefundRequest)
        .attach_printable_lazy(|| {
            format!(
                "Failed while inserting task in process_tracker: refund_id: {}",
                refund.refund_id
            )
        })?;
    Ok(response)
}

pub async fn get_refund_sync_process_schedule_time(
    db: &dyn db::StorageInterface,
    connector: &str,
    merchant_id: &common_utils::id_type::MerchantId,
    retry_count: i32,
) -> Result<Option<time::PrimitiveDateTime>, errors::ProcessTrackerError> {
    let mapping: common_utils::errors::CustomResult<
        process_data::ConnectorPTMapping,
        errors::StorageError,
    > = db
        .find_config_by_key(&format!("pt_mapping_refund_sync_{connector}"))
        .await
        .map(|value| value.config)
        .and_then(|config| {
            config
                .parse_struct("ConnectorPTMapping")
                .change_context(errors::StorageError::DeserializationFailed)
        });

    let mapping = match mapping {
        Ok(x) => x,
        Err(err) => {
            logger::error!("Error: while getting connector mapping: {err:?}");
            process_data::ConnectorPTMapping::default()
        }
    };

    let time_delta = process_tracker_utils::get_schedule_time(mapping, merchant_id, retry_count);

    Ok(process_tracker_utils::get_time_from_delta(time_delta))
}


// Function: get_connector_params
// File: crates/hyperswitch_domain_models/src/connector_endpoints.rs
// Module: hyperswitch_domain_models

pub fn get_connector_params(
        &self,
        connector: connector_enums::Connector,
    ) -> CustomResult<ConnectorParams, api_error_response::ApiErrorResponse>


// File: crates/router/src/compatibility/stripe/refunds/types.rs
// Module: router
// Public structs: 3

use std::{convert::From, default::Default};

use common_utils::pii;
use serde::{Deserialize, Serialize};

use crate::types::api::{admin, refunds};

#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct StripeCreateRefundRequest {
    pub refund_id: Option<String>,
    pub amount: Option<i64>,
    pub payment_intent: common_utils::id_type::PaymentId,
    pub reason: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
}

#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct StripeUpdateRefundRequest {
    #[serde(skip)]
    pub refund_id: String,
    pub metadata: Option<pii::SecretSerdeValue>,
}

#[derive(Clone, Serialize, PartialEq, Eq, Debug)]
pub struct StripeRefundResponse {
    pub id: String,
    pub amount: i64,
    pub currency: String,
    pub payment_intent: common_utils::id_type::PaymentId,
    pub status: StripeRefundStatus,
    pub created: Option<i64>,
    pub metadata: pii::SecretSerdeValue,
}

#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum StripeRefundStatus {
    Succeeded,
    Failed,
    Pending,
    RequiresAction,
}

impl From<StripeCreateRefundRequest> for refunds::RefundRequest {
    fn from(req: StripeCreateRefundRequest) -> Self {
        Self {
            refund_id: req.refund_id,
            amount: req.amount.map(common_utils::types::MinorUnit::new),
            payment_id: req.payment_intent,
            reason: req.reason,
            refund_type: Some(refunds::RefundType::Instant),
            metadata: req.metadata,
            merchant_connector_details: req.merchant_connector_details,
            ..Default::default()
        }
    }
}

impl From<StripeUpdateRefundRequest> for refunds::RefundUpdateRequest {
    fn from(req: StripeUpdateRefundRequest) -> Self {
        Self {
            refund_id: req.refund_id,
            metadata: req.metadata,
            reason: None,
        }
    }
}

impl From<refunds::RefundStatus> for StripeRefundStatus {
    fn from(status: refunds::RefundStatus) -> Self {
        match status {
            refunds::RefundStatus::Succeeded => Self::Succeeded,
            refunds::RefundStatus::Failed => Self::Failed,
            refunds::RefundStatus::Pending => Self::Pending,
            refunds::RefundStatus::Review => Self::RequiresAction,
        }
    }
}

impl From<refunds::RefundResponse> for StripeRefundResponse {
    fn from(res: refunds::RefundResponse) -> Self {
        Self {
            id: res.refund_id,
            amount: res.amount.get_amount_as_i64(),
            currency: res.currency.to_ascii_lowercase(),
            payment_intent: res.payment_id,
            status: res.status.into(),
            created: res.created_at.map(|t| t.assume_utc().unix_timestamp()),
            metadata: res
                .metadata
                .unwrap_or_else(|| masking::Secret::new(serde_json::json!({}))),
        }
    }
}


// Struct: Cardholder
// File: crates/hyperswitch_connectors/src/connectors/netcetera/netcetera_types.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Cardholder


// Implementation: impl ConnectorCommon for for Chargebee
// File: crates/hyperswitch_connectors/src/connectors/chargebee.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl ConnectorCommon for for Chargebee


// Function: get_api_event_filter_for_dimension
// File: crates/analytics/src/api_event/filters.rs
// Module: analytics

pub fn get_api_event_filter_for_dimension<T>(
    dimension: ApiEventDimensions,
    merchant_id: &common_utils::id_type::MerchantId,
    time_range: &TimeRange,
    pool: &T,
) -> FiltersResult<Vec<ApiEventFilter>>
where
    T: AnalyticsDataSource + ApiEventFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,


// Function: retrieve_disputes_list_profile
// File: crates/router/src/routes/disputes.rs
// Module: router

pub fn retrieve_disputes_list_profile(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Query<dispute_models::DisputeListGetConstraints>,
) -> HttpResponse


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/peachpayments.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Function: convert_forex
// File: crates/router/src/routes/currency.rs
// Module: router

pub fn convert_forex(
    state: web::Data<AppState>,
    req: HttpRequest,
    params: web::Query<api_models::currency::CurrencyConversionParams>,
) -> HttpResponse


// Struct: ChargebeeCardDetails
// File: crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ChargebeeCardDetails


// Implementation: impl AuthorizationNew
// File: crates/diesel_models/src/query/authorization.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl AuthorizationNew


// File: crates/router/src/types/storage/revenue_recovery.rs
// Module: router
// Public functions: 2
// Public structs: 6

use std::{collections::HashMap, fmt::Debug};

use common_enums::enums::{self, CardNetwork};
use common_utils::{date_time, ext_traits::ValueExt, id_type};
use error_stack::ResultExt;
use external_services::grpc_client::{self as external_grpc_client, GrpcHeaders};
use hyperswitch_domain_models::{
    business_profile, merchant_account, merchant_connector_account, merchant_key_store,
    payment_method_data::{Card, PaymentMethodData},
    payments::{payment_attempt::PaymentAttempt, PaymentIntent, PaymentStatusData},
};
use masking::PeekInterface;
use router_env::logger;
use serde::{Deserialize, Serialize};

use crate::{db::StorageInterface, routes::SessionState, types, workflows::revenue_recovery};
#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct RevenueRecoveryWorkflowTrackingData {
    pub merchant_id: id_type::MerchantId,
    pub profile_id: id_type::ProfileId,
    pub global_payment_id: id_type::GlobalPaymentId,
    pub payment_attempt_id: id_type::GlobalAttemptId,
    pub billing_mca_id: id_type::MerchantConnectorAccountId,
    pub revenue_recovery_retry: enums::RevenueRecoveryAlgorithmType,
    pub invoice_scheduled_time: Option<time::PrimitiveDateTime>,
}

#[derive(Debug, Clone)]
pub struct RevenueRecoveryPaymentData {
    pub merchant_account: merchant_account::MerchantAccount,
    pub profile: business_profile::Profile,
    pub key_store: merchant_key_store::MerchantKeyStore,
    pub billing_mca: merchant_connector_account::MerchantConnectorAccount,
    pub retry_algorithm: enums::RevenueRecoveryAlgorithmType,
    pub psync_data: Option<PaymentStatusData<types::api::PSync>>,
}
impl RevenueRecoveryPaymentData {
    pub async fn get_schedule_time_based_on_retry_type(
        &self,
        state: &SessionState,
        merchant_id: &id_type::MerchantId,
        retry_count: i32,
        payment_attempt: &PaymentAttempt,
        payment_intent: &PaymentIntent,
        is_hard_decline: bool,
    ) -> Option<time::PrimitiveDateTime> {
        if is_hard_decline {
            logger::info!("Hard Decline encountered");
            return None;
        }
        match self.retry_algorithm {
            enums::RevenueRecoveryAlgorithmType::Monitoring => {
                logger::error!("Monitoring type found for Revenue Recovery retry payment");
                None
            }
            enums::RevenueRecoveryAlgorithmType::Cascading => {
                logger::info!("Cascading type found for Revenue Recovery retry payment");
                revenue_recovery::get_schedule_time_to_retry_mit_payments(
                    state.store.as_ref(),
                    merchant_id,
                    retry_count,
                )
                .await
            }
            enums::RevenueRecoveryAlgorithmType::Smart => None,
        }
    }
}

#[derive(Debug, serde::Deserialize, Clone, Default)]
pub struct RevenueRecoverySettings {
    pub monitoring_threshold_in_seconds: i64,
    pub retry_algorithm_type: enums::RevenueRecoveryAlgorithmType,
    pub recovery_timestamp: RecoveryTimestamp,
    pub card_config: RetryLimitsConfig,
    pub redis_ttl_in_seconds: i64,
}

#[derive(Debug, serde::Deserialize, Clone)]
pub struct RecoveryTimestamp {
    pub initial_timestamp_in_seconds: i64,
    pub job_schedule_buffer_time_in_seconds: i64,
    pub reopen_workflow_buffer_time_in_seconds: i64,
    pub max_random_schedule_delay_in_seconds: i64,
    pub redis_ttl_buffer_in_seconds: i64,
}

impl Default for RecoveryTimestamp {
    fn default() -> Self {
        Self {
            initial_timestamp_in_seconds: 1,
            job_schedule_buffer_time_in_seconds: 15,
            reopen_workflow_buffer_time_in_seconds: 60,
            max_random_schedule_delay_in_seconds: 300,
            redis_ttl_buffer_in_seconds: 300,
        }
    }
}

#[derive(Debug, serde::Deserialize, Clone, Default)]
pub struct RetryLimitsConfig(pub HashMap<CardNetwork, NetworkRetryConfig>);

#[derive(Debug, serde::Deserialize, Clone, Default)]
pub struct NetworkRetryConfig {
    pub max_retries_per_day: i32,
    pub max_retry_count_for_thirty_day: i32,
}

impl RetryLimitsConfig {
    pub fn get_network_config(&self, network: Option<CardNetwork>) -> &NetworkRetryConfig {
        // Hardcoded fallback default config
        static DEFAULT_CONFIG: NetworkRetryConfig = NetworkRetryConfig {
            max_retries_per_day: 20,
            max_retry_count_for_thirty_day: 20,
        };

        if let Some(net) = network {
            self.0.get(&net).unwrap_or(&DEFAULT_CONFIG)
        } else {
            self.0.get(&CardNetwork::Visa).unwrap_or(&DEFAULT_CONFIG)
        }
    }
}


// Implementation: impl api::PaymentAuthorize for for Powertranz
// File: crates/hyperswitch_connectors/src/connectors/powertranz.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Powertranz


// Implementation: impl api::ConnectorAccessToken for for Fiserv
// File: crates/hyperswitch_connectors/src/connectors/fiserv.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ConnectorAccessToken for for Fiserv


// Struct: TaxObject
// File: crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TaxObject


// Struct: SignedMessage
// File: crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SignedMessage


// Implementation: impl webhooks::IncomingWebhook for for Coingate
// File: crates/hyperswitch_connectors/src/connectors/coingate.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl webhooks::IncomingWebhook for for Coingate


// Struct: MerchantAccountDeleteResponse
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 0

pub struct MerchantAccountDeleteResponse


// Implementation: impl LockerMockUpInterface for for KafkaStore
// File: crates/router/src/db/kafka_store.rs
// Module: router
// Methods: 3 total (0 public)

impl LockerMockUpInterface for for KafkaStore


// Implementation: impl Responder
// File: crates/router/src/routes/customers.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// File: crates/hyperswitch_interfaces/src/connector_integration_v2.rs
// Module: hyperswitch_interfaces

//! definition of the new connector integration trait

use common_utils::{
    errors::CustomResult,
    request::{Method, Request, RequestBuilder, RequestContent},
};
use hyperswitch_domain_models::{router_data::ErrorResponse, router_data_v2::RouterDataV2};
use masking::Maskable;
use serde_json::json;

use crate::{
    api::{self, subscriptions_v2, CaptureSyncMethod},
    errors,
    events::connector_api_logs::ConnectorEvent,
    metrics, types, webhooks,
};

/// ConnectorV2 trait
pub trait ConnectorV2:
    Send
    + api::refunds_v2::RefundV2
    + api::payments_v2::PaymentV2
    + api::ConnectorRedirectResponse
    + webhooks::IncomingWebhook
    + api::ConnectorAuthenticationTokenV2
    + api::ConnectorAccessTokenV2
    + api::disputes_v2::DisputeV2
    + api::files_v2::FileUploadV2
    + api::ConnectorTransactionId
    + api::PayoutsV2
    + api::ConnectorVerifyWebhookSourceV2
    + api::FraudCheckV2
    + api::ConnectorMandateRevokeV2
    + api::authentication_v2::ExternalAuthenticationV2
    + api::UnifiedAuthenticationServiceV2
    + api::revenue_recovery_v2::RevenueRecoveryV2
    + api::ExternalVaultV2
    + subscriptions_v2::SubscriptionsV2
{
}
impl<
        T: api::refunds_v2::RefundV2
            + api::payments_v2::PaymentV2
            + api::ConnectorRedirectResponse
            + Send
            + webhooks::IncomingWebhook
            + api::ConnectorAuthenticationTokenV2
            + api::ConnectorAccessTokenV2
            + api::disputes_v2::DisputeV2
            + api::files_v2::FileUploadV2
            + api::ConnectorTransactionId
            + api::PayoutsV2
            + api::ConnectorVerifyWebhookSourceV2
            + api::FraudCheckV2
            + api::ConnectorMandateRevokeV2
            + api::authentication_v2::ExternalAuthenticationV2
            + api::UnifiedAuthenticationServiceV2
            + api::revenue_recovery_v2::RevenueRecoveryV2
            + api::ExternalVaultV2
            + subscriptions_v2::SubscriptionsV2,
    > ConnectorV2 for T
{
}

/// Alias for Box<&'static (dyn ConnectorV2 + Sync)>
pub type BoxedConnectorV2 = Box<&'static (dyn ConnectorV2 + Sync)>;

/// alias for Box of a type that implements trait ConnectorIntegrationV2
pub type BoxedConnectorIntegrationV2<'a, Flow, ResourceCommonData, Req, Resp> =
    Box<&'a (dyn ConnectorIntegrationV2<Flow, ResourceCommonData, Req, Resp> + Send + Sync)>;

/// trait with a function that returns BoxedConnectorIntegrationV2
pub trait ConnectorIntegrationAnyV2<Flow, ResourceCommonData, Req, Resp>:
    Send + Sync + 'static
{
    /// function what returns BoxedConnectorIntegrationV2
    fn get_connector_integration_v2(
        &self,
    ) -> BoxedConnectorIntegrationV2<'_, Flow, ResourceCommonData, Req, Resp>;
}

impl<S, Flow, ResourceCommonData, Req, Resp>
    ConnectorIntegrationAnyV2<Flow, ResourceCommonData, Req, Resp> for S
where
    S: ConnectorIntegrationV2<Flow, ResourceCommonData, Req, Resp> + Send + Sync,
{
    fn get_connector_integration_v2(
        &self,
    ) -> BoxedConnectorIntegrationV2<'_, Flow, ResourceCommonData, Req, Resp> {
        Box::new(self)
    }
}

/// The new connector integration trait with an additional ResourceCommonData generic parameter
pub trait ConnectorIntegrationV2<Flow, ResourceCommonData, Req, Resp>:
    ConnectorIntegrationAnyV2<Flow, ResourceCommonData, Req, Resp> + Sync + api::ConnectorCommon
{
    /// returns a vec of tuple of header key and value
    fn get_headers(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        Ok(vec![])
    }

    /// returns content type
    fn get_content_type(&self) -> &'static str {
        mime::APPLICATION_JSON.essence_str()
    }

    /// primarily used when creating signature based on request method of payment flow
    fn get_http_method(&self) -> Method {
        Method::Post
    }

    /// returns url
    fn get_url(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<String, errors::ConnectorError> {
        metrics::UNIMPLEMENTED_FLOW
            .add(1, router_env::metric_attributes!(("connector", self.id())));
        Ok(String::new())
    }

    /// returns request body
    fn get_request_body(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<RequestContent>, errors::ConnectorError> {
        Ok(None)
    }

    /// returns form data
    fn get_request_form_data(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<reqwest::multipart::Form>, errors::ConnectorError> {
        Ok(None)
    }

    /// builds the request and returns it
    fn build_request_v2(
        &self,
        req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(self.get_http_method())
                .url(self.get_url(req)?.as_str())
                .attach_default_headers()
                .headers(self.get_headers(req)?)
                .set_optional_body(self.get_request_body(req)?)
                .add_certificate(self.get_certificate(req)?)
                .add_certificate_key(self.get_certificate_key(req)?)
                .build(),
        ))
    }

    /// accepts the raw api response and decodes it
    fn handle_response_v2(
        &self,
        data: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
        event_builder: Option<&mut ConnectorEvent>,
        _res: types::Response,
    ) -> CustomResult<RouterDataV2<Flow, ResourceCommonData, Req, Resp>, errors::ConnectorError>
    where
        Flow: Clone,
        ResourceCommonData: Clone,
        Req: Clone,
        Resp: Clone,
    {
        event_builder.map(|e| e.set_error(json!({"error": "Not Implemented"})));
        Ok(data.clone())
    }

    /// accepts the raw api error response and decodes it
    fn get_error_response_v2(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        event_builder.map(|event| event.set_error(json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
        Ok(ErrorResponse::get_not_implemented())
    }

    /// accepts the raw 5xx error response and decodes it
    fn get_5xx_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        event_builder.map(|event| event.set_error(json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
        let error_message = match res.status_code {
            500 => "internal_server_error",
            501 => "not_implemented",
            502 => "bad_gateway",
            503 => "service_unavailable",
            504 => "gateway_timeout",
            505 => "http_version_not_supported",
            506 => "variant_also_negotiates",
            507 => "insufficient_storage",
            508 => "loop_detected",
            510 => "not_extended",
            511 => "network_authentication_required",
            _ => "unknown_error",
        };
        Ok(ErrorResponse {
            code: res.status_code.to_string(),
            message: error_message.to_string(),
            reason: String::from_utf8(res.response.to_vec()).ok(),
            status_code: res.status_code,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }

    // whenever capture sync is implemented at the connector side, this method should be overridden
    /// retunes the capture sync method
    fn get_multiple_capture_sync_method(
        &self,
    ) -> CustomResult<CaptureSyncMethod, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("multiple capture sync".into()).into())
    }

    /// returns certificate string
    fn get_certificate(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<masking::Secret<String>>, errors::ConnectorError> {
        Ok(None)
    }

    /// returns private key string
    fn get_certificate_key(
        &self,
        _req: &RouterDataV2<Flow, ResourceCommonData, Req, Resp>,
    ) -> CustomResult<Option<masking::Secret<String>>, errors::ConnectorError> {
        Ok(None)
    }
}