File: crates/storage_impl/src/payouts.rs

pub mod payout_attempt;
#[allow(clippy::module_inception)]
pub mod payouts;

use diesel_models::{payout_attempt::PayoutAttempt, payouts::Payouts};

use crate::redis::kv_store::KvStorePartition;

impl KvStorePartition for Payouts {}
impl KvStorePartition for PayoutAttempt {}


pub struct FiservemeaPaymentCard {
    number: cards::CardNumber,
    expiry_date: FiservemeaExpiryDate,
    security_code: Secret<String>,
}


pub struct Card {
    pub cvv: Secret<String>,
    pub expiry_month: Secret<String>,
    pub expiry_year: Secret<String>,
    pub number: cards::CardNumber,
}


impl ConnectorValidation for Barclaycard {
    //TODO: implement functions when support enabled
}

impl ConnectorValidation for for Barclaycard


pub struct ThreeDSecureIoConnectorMetaData {
    pub ds_start_protocol_version: String,
    pub ds_end_protocol_version: String,
    pub acs_start_protocol_version: String,
    pub acs_end_protocol_version: String,
}


pub struct ApiPayload {
    pub service: String,
    pub card_data: Secret<String>, //encrypted card data
    pub order_data: OrderData,
    pub key_id: String,
    pub should_send_token: bool,
}


impl ErrorCategory {
    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,
        }
    }
}


impl api::PaymentCapture for Payme {}


pub struct CallbackMapper {
    pub id: String,
    pub callback_mapper_id_type: common_enums::CallbackMapperIdType,
    pub data: CallbackMapperData,
    pub created_at: time::PrimitiveDateTime,
    pub last_modified_at: time::PrimitiveDateTime,
}


pub fn get_payment_method_type(&self) -> Option<&Vec<RequestPaymentMethodTypes>> {
        self.payment_method_subtypes.as_ref()
    }


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


pub struct CardToken {
    /// The card holder's name
    pub card_holder_name: Option<Secret<String>>,

    /// The CVC number for the card
    pub card_cvc: Option<Secret<String>>,
}


pub struct RecipientBankAccountRequest {
    #[serde(rename = "external_account[object]")]
    external_account_object: String,
    #[serde(rename = "external_account[country]")]
    external_account_country: enums::CountryAlpha2,
    #[serde(rename = "external_account[currency]")]
    external_account_currency: Currency,
    #[serde(rename = "external_account[account_holder_name]")]
    external_account_account_holder_name: Secret<String>,
    #[serde(rename = "external_account[account_number]")]
    external_account_account_number: Secret<String>,
    #[serde(rename = "external_account[account_holder_type]")]
    external_account_account_holder_type: String,
    #[serde(rename = "external_account[routing_number]")]
    external_account_routing_number: Secret<String>,
}


impl ConnectorValidation for Nmi {
    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), ConnectorError> {
        // in case we dont have transaction id, we can make psync using attempt id
        Ok(())
    }

    fn validate_mandate_payment(
        &self,
        pm_type: Option<enums::PaymentMethodType>,
        pm_data: hyperswitch_domain_models::payment_method_data::PaymentMethodData,
    ) -> CustomResult<(), ConnectorError> {
        let mandate_supported_pmd =
            std::collections::HashSet::from([utils::PaymentMethodDataType::Card]);
        utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id())
    }
}


impl super::KafkaMessage for KafkaFraudCheck<'_> {
    fn key(&self) -> String {
        format!(
            "{}_{}_{}_{}",
            self.merchant_id.get_string_repr(),
            self.payment_id.get_string_repr(),
            self.attempt_id,
            self.frm_id
        )
    }

    fn event_type(&self) -> crate::events::EventType {
        crate::events::EventType::FraudCheck
    }
}

impl super::KafkaMessage for for KafkaFraudCheck


impl api::PaymentAuthorize for Dlocal {}

impl api::PaymentAuthorize for for Dlocal


pub fn build_unified_connector_service_auth_metadata(
    #[cfg(feature = "v1")


pub struct SantanderPixVoidResponse {
    #[serde(rename = "calendario")]
    pub calendar: SantanderCalendar,
    #[serde(rename = "txid")]
    pub transaction_id: String,
    #[serde(rename = "revisao")]
    pub revision: i32,
    #[serde(rename = "devedor")]
    pub debtor: Option<SantanderDebtor>,
    pub location: Option<String>,
    pub status: SantanderPaymentStatus,
    #[serde(rename = "valor")]
    pub value: SantanderValue,
    #[serde(rename = "chave")]
    pub key: Secret<String>,
    #[serde(rename = "solicitacaoPagador")]
    pub request_payer: Option<String>,
    #[serde(rename = "infoAdicionais")]
    pub additional_info: Option<Vec<SantanderAdditionalInfo>>,
}


pub struct ReqState {
    pub event_context: events::EventContext<crate::events::EventType, EventsHandler>,
}


impl RecoveryDeciderClientConfig {
    /// Validate the configuration
    pub fn validate(&self) -> Result<(), RecoveryDeciderError> {
        use common_utils::fp_utils::when;

        when(self.base_url.is_empty(), || {
            Err(RecoveryDeciderError::ConfigError(
                "Recovery Decider base URL cannot be empty when configuration is provided"
                    .to_string(),
            ))
        })
    }

    /// create a connection
    pub fn get_recovery_decider_connection(
        &self,
        hyper_client: Client,
    ) -> Result<DeciderClient<Client>, Report<RecoveryDeciderError>> {
        let uri = self
            .base_url
            .parse::<tonic::transport::Uri>()
            .map_err(Report::from)
            .change_context(RecoveryDeciderError::ConfigError(format!(
                "Invalid URI: {}",
                self.base_url
            )))?;

        let service_client = DeciderClient::with_origin(hyper_client, uri);

        Ok(service_client)
    }
}


pub struct Card {
    pub card_number: StrongSecret<String>,
}


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

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

    fn common_get_content_type(&self) -> &'static str {
        "application/x-www-form-urlencoded"
    }

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

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response = serde_urlencoded::from_bytes::<zsl::ZslErrorResponse>(&res.response)
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        let error_reason = zsl::ZslResponseStatus::try_from(response.status.clone())?.to_string();

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.status,
            message: error_reason.clone(),
            reason: Some(error_reason),
            attempt_status: Some(common_enums::AttemptStatus::Failure),
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


File: crates/hyperswitch_interfaces/src/secrets_interface/secret_state.rs
 Public functions: 3
 Public structs: 3

//! Module to manage encrypted and decrypted states for a given type.

use std::marker::PhantomData;

use serde::{Deserialize, Deserializer};

/// Trait defining the states of a secret
pub trait SecretState {}

/// Decrypted state
#[derive(Debug, Clone, Deserialize, Default)]
pub struct RawSecret {}

/// Encrypted state
#[derive(Debug, Clone, Deserialize, Default)]
pub struct SecuredSecret {}

impl SecretState for RawSecret {}
impl SecretState for SecuredSecret {}

/// Struct for managing the encrypted and decrypted states of a given type
#[derive(Debug, Clone, Default)]
pub struct SecretStateContainer<T, S: SecretState> {
    inner: T,
    marker: PhantomData<S>,
}

impl<T: Clone, S: SecretState> SecretStateContainer<T, S> {
    /// Get the inner data while consuming self
    #[inline]
    pub fn into_inner(self) -> T {
        self.inner
    }

    /// Get the reference to inner value
    #[inline]
    pub fn get_inner(&self) -> &T {
        &self.inner
    }
}

impl<'de, T: Deserialize<'de>, S: SecretState> Deserialize<'de> for SecretStateContainer<T, S> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let val = Deserialize::deserialize(deserializer)?;
        Ok(Self {
            inner: val,
            marker: PhantomData,
        })
    }
}

impl<T> SecretStateContainer<T, SecuredSecret> {
    /// Transition the secret state from `SecuredSecret` to `RawSecret`
    pub fn transition_state(
        mut self,
        decryptor_fn: impl FnOnce(T) -> T,
    ) -> SecretStateContainer<T, RawSecret> {
        self.inner = decryptor_fn(self.inner);
        SecretStateContainer {
            inner: self.inner,
            marker: PhantomData,
        }
    }
}


pub async fn call_update_gateway_score_open_router() {}


pub struct ThreeDSPaymentsRequest {
    id_merchant: Secret<String>,
    id_session: String,
    amount: MinorUnit,
    currency: String,
    #[serde(rename = "CCNumber")]
    cc_number: cards::CardNumber,
    #[serde(rename = "CCExpDate")]
    cc_exp_date: Secret<String>,
    #[serde(rename = "CVVCode")]
    cvv_code: Secret<String>,
    #[serde(rename = "URLRetour")]
    url_retour: String,
    #[serde(rename = "URLHttpDirect")]
    url_http_direct: String,
    email_porteur: common_utils::pii::Email,
    first_name: Secret<String>,
    last_name: Secret<String>,
    address1: Secret<String>,
    zip_code: Secret<String>,
    city: String,
    country_code: String,
    total_quantity: i32,
}


pub async fn convert_currency(
    state: SessionState,
    amount: i64,
    to_currency: String,
    from_currency: String,
) -> CustomResult<api_models::currency::CurrencyConversionResponse, ForexError> {
    let forex_api = state.conf.forex_api.get_inner();
    let rates = get_forex_rates(&state, forex_api.data_expiration_delay_in_seconds)
        .await
        .change_context(ForexError::ApiError)?;

    let to_currency = enums::Currency::from_str(to_currency.as_str())
        .change_context(ForexError::CurrencyNotAcceptable)
        .attach_printable("The provided currency is not acceptable")?;

    let from_currency = enums::Currency::from_str(from_currency.as_str())
        .change_context(ForexError::CurrencyNotAcceptable)
        .attach_printable("The provided currency is not acceptable")?;

    let converted_amount =
        currency_conversion::conversion::convert(&rates.data, from_currency, to_currency, amount)
            .change_context(ForexError::ConversionError)
            .attach_printable("Unable to perform currency conversion")?;

    Ok(api_models::currency::CurrencyConversionResponse {
        converted_amount: converted_amount.to_string(),
        currency: to_currency.to_string(),
    })
}


OpenAPI Block Path: components.schemas.SamsungPaySessionTokenResponse
{
  "type": "object",
  "required": [
    "version",
    "service_id",
    "order_number",
    "merchant",
    "amount",
    "protocol",
    "allowed_brands",
    "billing_address_required",
    "shipping_address_required"
  ],
  "properties": {
    "version": {
      "type": "string",
      "description": "Samsung Pay API version"
    },
    "service_id": {
      "type": "string",
      "description": "Samsung Pay service ID to which session call needs to be made"
    },
    "order_number": {
      "type": "string",
      "description": "Order number of the transaction"
    },
    "merchant": {
      "$ref": "#/components/schemas/SamsungPayMerchantPaymentInformation"
    },
    "amount": {
      "$ref": "#/components/schemas/SamsungPayAmountDetails"
    },
    "protocol": {
      "$ref": "#/components/schemas/SamsungPayProtocolType"
    },
    "allowed_brands": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "List of supported card brands"
    },
    "billing_address_required": {
      "type": "boolean",
      "description": "Is billing address required to be collected from wallet"
    },
    "shipping_address_required": {
      "type": "boolean",
      "description": "Is shipping address required to be collected from wallet"
    }
  }
}


impl api::Payouts for Adyenplatform {}


File: crates/diesel_models/src/query/address.rs
 Public functions: 8

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};

use super::generics;
use crate::{
    address::{Address, AddressNew, AddressUpdateInternal},
    errors,
    schema::address::dsl,
    PgPooledConn, StorageResult,
};

impl AddressNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Address> {
        generics::generic_insert(conn, self).await
    }
}

impl Address {
    pub async fn find_by_address_id(conn: &PgPooledConn, address_id: &str) -> StorageResult<Self> {
        generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(conn, address_id.to_owned())
            .await
    }

    pub async fn update_by_address_id(
        conn: &PgPooledConn,
        address_id: String,
        address: AddressUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            address_id.clone(),
            address,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NotFound => {
                    Err(error.attach_printable("Address with the given ID doesn't exist"))
                }
                errors::DatabaseError::NoFieldsToUpdate => {
                    generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(
                        conn,
                        address_id.clone(),
                    )
                    .await
                }
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn update(
        self,
        conn: &PgPooledConn,
        address_update_internal: AddressUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::address_id.eq(self.address_id.clone()),
            address_update_internal,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => Ok(self),
                _ => Err(error),
            },
            result => result,
        }
    }

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

    pub async fn update_by_merchant_id_customer_id(
        conn: &PgPooledConn,
        customer_id: &common_utils::id_type::CustomerId,
        merchant_id: &common_utils::id_type::MerchantId,
        address: AddressUpdateInternal,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::customer_id.eq(customer_id.to_owned())),
            address,
        )
        .await
    }

    pub async fn find_by_merchant_id_payment_id_address_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
        address_id: &str,
    ) -> StorageResult<Self> {
        match generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::payment_id
                .eq(payment_id.to_owned())
                .and(dsl::merchant_id.eq(merchant_id.to_owned()))
                .and(dsl::address_id.eq(address_id.to_owned())),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NotFound => {
                    generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(
                        conn,
                        address_id.to_owned(),
                    )
                    .await
                }
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn find_optional_by_address_id(
        conn: &PgPooledConn,
        address_id: &str,
    ) -> StorageResult<Option<Self>> {
        generics::generic_find_by_id_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            address_id.to_owned(),
        )
        .await
    }
}


pub fn get_network_token_resource_object(
    request_details: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::NetworkTokenizationError> {
    let response: NetworkTokenWebhookResponse = request_details
        .body
        .parse_struct("NetworkTokenWebhookResponse")
        .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
    Ok(Box::new(response))
}


pub struct GlobalpayRefreshTokenErrorResponse {
    pub error_code: String,
    pub detailed_error_description: String,
}


pub const fn new() -> &'static Self {
        &Self {
            amount_converter: &MinorUnitForConnector,
            amount_converter_webhooks: &StringMinorUnitForConnector,
        }
    }


pub struct DigitalvirgoPaymentSyncResponse {
    payment_status: DigitalvirgoPaymentSyncStatus,
    transaction_id: String,
}


impl scheduler::SchedulerAppState for AppState {
    fn get_tenants(&self) -> Vec<id_type::TenantId> {
        self.conf.multitenancy.get_tenant_ids()
    }
}

impl scheduler::SchedulerAppState for for AppState


pub async fn retrieve_merchant_routing_dictionary(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
    query_params: RoutingRetrieveQuery,
    transaction_type: enums::TransactionType,
) -> RouterResponse<routing_types::RoutingKind> {
    metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE.add(1, &[]);

    let routing_metadata: Vec<diesel_models::routing_algorithm::RoutingProfileMetadata> = state
        .store
        .list_routing_algorithm_metadata_by_merchant_id_transaction_type(
            merchant_context.get_merchant_account().get_id(),
            &transaction_type,
            i64::from(query_params.limit.unwrap_or_default()),
            i64::from(query_params.offset.unwrap_or_default()),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
    let routing_metadata = super::utils::filter_objects_based_on_profile_id_list(
        profile_id_list.clone(),
        routing_metadata,
    );

    let mut result = routing_metadata
        .into_iter()
        .map(ForeignInto::foreign_into)
        .collect::<Vec<_>>();

    if let Some(profile_ids) = profile_id_list {
        let mut de_result: Vec<routing_types::RoutingDictionaryRecord> = vec![];
        // DE_TODO: need to replace this with batch API call to reduce the number of network calls
        for profile_id in &profile_ids {
            let list_request = ListRountingAlgorithmsRequest {
                created_by: profile_id.get_string_repr().to_string(),
            };
            list_de_euclid_routing_algorithms(&state, list_request)
                .await
                .map_err(|e| {
                    router_env::logger::error!(decision_engine_error=?e, "decision_engine_euclid");
                })
                .ok() // Avoid throwing error if Decision Engine is not available or other errors
                .map(|mut de_routing| de_result.append(&mut de_routing));
            // filter de_result based on transaction type
            de_result.retain(|record| record.algorithm_for == Some(transaction_type));
            // append dynamic routing algorithms to de_result
            de_result.append(
                &mut result
                    .clone()
                    .into_iter()
                    .filter(|record: &routing_types::RoutingDictionaryRecord| {
                        record.kind == routing_types::RoutingAlgorithmKind::Dynamic
                    })
                    .collect::<Vec<_>>(),
            );
        }
        compare_and_log_result(
            de_result.clone(),
            result.clone(),
            "list_routing".to_string(),
        );
        result = build_list_routing_result(
            &state,
            merchant_context,
            &result,
            &de_result,
            profile_ids.clone(),
        )
        .await?;
    }

    metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(
        routing_types::RoutingKind::RoutingAlgorithm(result),
    ))
}


pub async fn payments_reject(
    state: web::Data<app::AppState>,
    http_req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsRejectRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let mut payload = json_payload.into_inner();
    let payment_id = path.into_inner();

    tracing::Span::current().record("payment_id", payment_id.get_string_repr());

    payload.payment_id = payment_id;
    let flow = Flow::PaymentsReject;
    let fpayload = FPaymentsRejectRequest(&payload);
    let locking_action = fpayload.get_locking_input(flow.clone());

    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &http_req,
        payload.clone(),
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::Void,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Void>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentReject,
                payment_types::PaymentsCancelRequest {
                    payment_id: req.payment_id,
                    cancellation_reason: Some("Rejected by merchant".to_string()),
                    ..Default::default()
                },
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                HeaderPayload::default(),
            )
        },
        match env::which() {
            env::Env::Production => &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: true,
            }),
            _ => auth::auth_type(
                &auth::HeaderAuth(auth::ApiKeyAuth {
                    is_connected_allowed: false,
                    is_platform_allowed: true,
                }),
                &auth::JWTAuth {
                    permission: Permission::ProfilePaymentWrite,
                },
                http_req.headers(),
            ),
        },
        locking_action,
    ))
    .await
}


impl api::PaymentSession for Braintree {}

impl api::PaymentSession for for Braintree


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

use std::marker::PhantomData;

use api_models::enums::FrmSuggestion;
use async_trait::async_trait;
use common_utils::ext_traits::AsyncExt;
use error_stack::ResultExt;
use router_env::{instrument, tracing};

use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
use crate::{
    core::{
        errors::{self, RouterResult, StorageErrorExt},
        payments::{self, helpers, operations, types::MultipleCaptureData},
    },
    events::audit_events::{AuditEvent, AuditEventType},
    routes::{app::ReqState, SessionState},
    services,
    types::{
        self as core_types,
        api::{self, PaymentIdTypeExt},
        domain,
        storage::{self, enums, payment_attempt::PaymentAttemptExt},
    },
    utils::OptionExt,
};

#[derive(Debug, Clone, Copy, router_derive::PaymentOperation)]
#[operation(operations = "all", flow = "capture")]
pub struct PaymentCapture;

type PaymentCaptureOperation<'b, F> =
    BoxedOperation<'b, F, api::PaymentsCaptureRequest, payments::PaymentData<F>>;

#[async_trait]
impl<F: Send + Clone + Sync> GetTracker<F, payments::PaymentData<F>, api::PaymentsCaptureRequest>
    for PaymentCapture
{
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &api::PaymentIdType,
        request: &api::PaymentsCaptureRequest,
        merchant_context: &domain::MerchantContext,
        _auth_flow: services::AuthFlow,
        _header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<
        operations::GetTrackerResponse<
            'a,
            F,
            api::PaymentsCaptureRequest,
            payments::PaymentData<F>,
        >,
    > {
        let db = &*state.store;
        let key_manager_state = &state.into();

        let merchant_id = merchant_context.get_merchant_account().get_id();
        let storage_scheme = merchant_context.get_merchant_account().storage_scheme;
        let (payment_intent, mut payment_attempt, currency, amount);

        let payment_id = payment_id
            .get_payment_intent_id()
            .change_context(errors::ApiErrorResponse::PaymentNotFound)?;

        payment_intent = db
            .find_payment_intent_by_payment_id_merchant_id(
                key_manager_state,
                &payment_id,
                merchant_id,
                merchant_context.get_merchant_key_store(),
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

        payment_attempt = db
            .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_intent.payment_id,
                merchant_id,
                payment_intent.active_attempt.get_id().as_str(),
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

        payment_attempt
            .amount_to_capture
            .update_value(request.amount_to_capture);

        let capture_method = payment_attempt
            .capture_method
            .get_required_value("capture_method")?;

        helpers::validate_status_with_capture_method(payment_intent.status, capture_method)?;

        helpers::validate_amount_to_capture(
            payment_attempt.amount_capturable.get_amount_as_i64(),
            request
                .amount_to_capture
                .map(|capture_amount| capture_amount.get_amount_as_i64()),
        )?;

        helpers::validate_capture_method(capture_method)?;

        let multiple_capture_data = if capture_method == enums::CaptureMethod::ManualMultiple {
            let amount_to_capture = request
                .amount_to_capture
                .get_required_value("amount_to_capture")?;

            helpers::validate_amount_to_capture(
                payment_attempt.amount_capturable.get_amount_as_i64(),
                Some(amount_to_capture.get_amount_as_i64()),
            )?;

            let previous_captures = db
                .find_all_captures_by_merchant_id_payment_id_authorized_attempt_id(
                    &payment_attempt.merchant_id,
                    &payment_attempt.payment_id,
                    &payment_attempt.attempt_id,
                    storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

            let capture = db
                .insert_capture(
                    payment_attempt
                        .make_new_capture(amount_to_capture, enums::CaptureStatus::Started)?,
                    storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::DuplicatePayment {
                    payment_id: payment_id.clone(),
                })?;

            Some(MultipleCaptureData::new_for_create(
                previous_captures,
                capture,
            ))
        } else {
            None
        };

        currency = payment_attempt.currency.get_required_value("currency")?;

        amount = payment_attempt.get_total_amount().into();

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

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

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

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

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

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

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

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

        Ok(get_trackers_response)
    }
}

#[async_trait]
impl<F: Clone + Sync> UpdateTracker<F, payments::PaymentData<F>, api::PaymentsCaptureRequest>
    for PaymentCapture
{
    #[instrument(skip_all)]
    async fn update_trackers<'b>(
        &'b self,
        db: &'b SessionState,
        req_state: ReqState,
        mut payment_data: payments::PaymentData<F>,
        _customer: Option<domain::Customer>,
        storage_scheme: enums::MerchantStorageScheme,
        _updated_customer: Option<storage::CustomerUpdate>,
        _mechant_key_store: &domain::MerchantKeyStore,
        _frm_suggestion: Option<FrmSuggestion>,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(PaymentCaptureOperation<'b, F>, payments::PaymentData<F>)>
    where
        F: 'b + Send,
    {
        payment_data.payment_attempt = if payment_data.multiple_capture_data.is_some()
            || payment_data.payment_attempt.amount_to_capture.is_some()
        {
            let multiple_capture_count = payment_data
                .multiple_capture_data
                .as_ref()
                .map(|multiple_capture_data| multiple_capture_data.get_captures_count())
                .transpose()?;
            let amount_to_capture = payment_data.payment_attempt.amount_to_capture;
            db.store
                .update_payment_attempt_with_attempt_id(
                    payment_data.payment_attempt,
                    storage::PaymentAttemptUpdate::CaptureUpdate {
                        amount_to_capture,
                        multiple_capture_count,
                        updated_by: storage_scheme.to_string(),
                    },
                    storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?
        } else {
            payment_data.payment_attempt
        };
        let capture_amount = payment_data.payment_attempt.amount_to_capture;
        let multiple_capture_count = payment_data.payment_attempt.multiple_capture_count;
        req_state
            .event_context
            .event(AuditEvent::new(AuditEventType::PaymentCapture {
                capture_amount,
                multiple_capture_count,
            }))
            .with(payment_data.to_event())
            .emit();
        Ok((Box::new(self), payment_data))
    }
}

impl<F: Send + Clone + Sync>
    ValidateRequest<F, api::PaymentsCaptureRequest, payments::PaymentData<F>> for PaymentCapture
{
    #[instrument(skip_all)]
    fn validate_request<'a, 'b>(
        &'b self,
        request: &api::PaymentsCaptureRequest,
        merchant_context: &'a domain::MerchantContext,
    ) -> RouterResult<(PaymentCaptureOperation<'b, F>, operations::ValidateResult)> {
        Ok((
            Box::new(self),
            operations::ValidateResult {
                merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
                payment_id: api::PaymentIdType::PaymentIntentId(request.payment_id.to_owned()),
                storage_scheme: merchant_context.get_merchant_account().storage_scheme,
                requeue: false,
            },
        ))
    }
}


pub struct ThreeDsEligibilityResponse {
    pub three_ds_server_trans_id: String,
    pub scheme_id: Option<String>,
    pub acs_protocol_versions: Option<Vec<AcsProtocolVersion>>,
    pub ds_protocol_versions: Option<Vec<String>>,
    pub three_ds_method_data_form: Option<ThreeDsMethodDataForm>,
    pub three_ds_method_data: Option<ThreeDsMethodData>,
    pub error_details: Option<String>,
    pub is_card_found_in_2x_ranges: bool,
    pub directory_server_id: Option<String>,
}


pub async fn create_payout_link(
    state: &SessionState,
    business_profile: &domain::Profile,
    customer_id: &id_type::CustomerId,
    merchant_id: &id_type::MerchantId,
    req: &payouts::PayoutCreateRequest,
    payout_id: &id_type::PayoutId,
    locale: &str,
) -> RouterResult<PayoutLink> {
    let payout_link_config_req = req.payout_link_config.to_owned();

    // Fetch all configs
    let default_config = &state.conf.generic_link.payout_link;
    let profile_config = &business_profile.payout_link_config;
    let profile_ui_config = profile_config.as_ref().map(|c| c.config.ui_config.clone());
    let ui_config = payout_link_config_req
        .as_ref()
        .and_then(|config| config.ui_config.clone())
        .or(profile_ui_config);

    let test_mode_in_config = payout_link_config_req
        .as_ref()
        .and_then(|config| config.test_mode)
        .or_else(|| profile_config.as_ref().and_then(|c| c.payout_test_mode));
    let is_test_mode_enabled = test_mode_in_config.unwrap_or(false);

    let allowed_domains = match (router_env::which(), is_test_mode_enabled) {
        // Throw error in case test_mode was enabled in production
        (Env::Production, true) => Err(report!(errors::ApiErrorResponse::LinkConfigurationError {
            message: "test_mode cannot be true for creating payout_links in production".to_string()
        })),
        // Send empty set of whitelisted domains
        (_, true) => {
            Ok(HashSet::new())
        },
        // Otherwise, fetch and use allowed domains from profile config
        (_, false) => {
            profile_config
            .as_ref()
            .map(|config| config.config.allowed_domains.to_owned())
            .get_required_value("allowed_domains")
            .change_context(errors::ApiErrorResponse::LinkConfigurationError {
                message:
                    "Payout links cannot be used without setting allowed_domains in profile. If you're using a non-production environment, you can set test_mode to true while in payout_link_config"
                        .to_string(),
            })
        }
    }?;

    // Form data to be injected in the link
    let (logo, merchant_name, theme) = match ui_config {
        Some(config) => (config.logo, config.merchant_name, config.theme),
        _ => (None, None, None),
    };
    let payout_link_config = GenericLinkUiConfig {
        logo,
        merchant_name,
        theme,
    };
    let client_secret = utils::generate_id(consts::ID_LENGTH, "payout_link_secret");
    let base_url = profile_config
        .as_ref()
        .and_then(|c| c.config.domain_name.as_ref())
        .map(|domain| format!("https://{domain}"))
        .unwrap_or(state.base_url.clone());
    let session_expiry = req
        .session_expiry
        .as_ref()
        .map_or(default_config.expiry, |expiry| *expiry);
    let url = format!(
        "{base_url}/payout_link/{}/{}?locale={}",
        merchant_id.get_string_repr(),
        payout_id.get_string_repr(),
        locale
    );
    let link = url::Url::parse(&url)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| format!("Failed to form payout link URL - {url}"))?;
    let req_enabled_payment_methods = payout_link_config_req
        .as_ref()
        .and_then(|req| req.enabled_payment_methods.to_owned());
    let amount = req
        .amount
        .as_ref()
        .get_required_value("amount")
        .attach_printable("amount is a required value when creating payout links")?;
    let currency = req
        .currency
        .as_ref()
        .get_required_value("currency")
        .attach_printable("currency is a required value when creating payout links")?;
    let payout_link_id = core_utils::get_or_generate_id(
        "payout_link_id",
        &payout_link_config_req
            .as_ref()
            .and_then(|config| config.payout_link_id.clone()),
        "payout_link",
    )?;
    let form_layout = payout_link_config_req
        .as_ref()
        .and_then(|config| config.form_layout.to_owned())
        .or_else(|| {
            profile_config
                .as_ref()
                .and_then(|config| config.form_layout.to_owned())
        });

    let data = PayoutLinkData {
        payout_link_id: payout_link_id.clone(),
        customer_id: customer_id.clone(),
        payout_id: payout_id.clone(),
        link,
        client_secret: Secret::new(client_secret),
        session_expiry,
        ui_config: payout_link_config,
        enabled_payment_methods: req_enabled_payment_methods,
        amount: MinorUnit::from(*amount),
        currency: *currency,
        allowed_domains,
        form_layout,
        test_mode: test_mode_in_config,
    };

    create_payout_link_db_entry(state, merchant_id, &data, req.return_url.clone()).await
}


pub fn validate_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);

    macros::misc::validate_config(input)
        .unwrap_or_else(|error| error.into_compile_error())
        .into()
}


impl api::PaymentVoid for Barclaycard {}

impl api::PaymentVoid for for Barclaycard


impl api::RefundSync for Coinbase {}


impl api::Refund for Razorpay {}


impl<const T: u8> PaymentAuthorize for DummyConnector<T> {}

impl PaymentAuthorize for for DummyConnector


pub async fn update_card_info(
    state: routes::SessionState,
    card_info_request: cards_info_api_types::CardInfoUpdateRequest,
) -> RouterResponse<cards_info_api_types::CardInfoResponse> {
    let db = state.store.as_ref();
    cards_info::CardsInfoInterface::update_card_info(
        db,
        card_info_request.card_iin,
        card_info_models::UpdateCardInfo {
            card_issuer: card_info_request.card_issuer,
            card_network: card_info_request.card_network,
            card_type: card_info_request.card_type,
            card_subtype: card_info_request.card_subtype,
            card_issuing_country: card_info_request.card_issuing_country,
            bank_code_id: card_info_request.bank_code_id,
            bank_code: card_info_request.bank_code,
            country_code: card_info_request.country_code,
            last_updated: Some(common_utils::date_time::now()),
            last_updated_provider: card_info_request.last_updated_provider,
        },
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
        message: "Card info with given key does not exist in our records".to_string(),
    })
    .attach_printable("Failed while updating card info")
    .map(|card_info| ApplicationResponse::Json(card_info.foreign_into()))
}


pub fn new(
        algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp<
            common_utils::id_type::RoutingId,
        >,
    ) -> Self {
        Self {
            algorithm_id_with_timestamp,
            enabled_feature: DynamicRoutingFeatures::None,
        }
    }


pub fn server(config: AppState) -> Scope {
        web::scope("/configs")
            .app_data(web::Data::new(config))
            .service(web::resource("/").route(web::post().to(config_key_create)))
            .service(
                web::resource("/{key}")
                    .route(web::get().to(config_key_retrieve))
                    .route(web::post().to(config_key_update))
                    .route(web::delete().to(config_key_delete)),
            )
    }


pub struct ConnectorParamsWithMoreUrls {
    /// base url
    pub base_url: String,
    /// base url for bank redirects
    pub base_url_bank_redirects: String,
}


File: crates/analytics/src/payments/metrics.rs
 Public structs: 1

use std::collections::HashSet;

use api_models::analytics::{
    payments::{PaymentDimensions, PaymentFilters, PaymentMetrics, PaymentMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use diesel_models::enums as storage_enums;
use time::PrimitiveDateTime;

use crate::{
    enums::AuthInfo,
    query::{Aggregate, GroupByClause, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, DBEnumWrapper, LoadRow, MetricsResult},
};

mod avg_ticket_size;
mod connector_success_rate;
mod debit_routing;
mod payment_count;
mod payment_processed_amount;
mod payment_success_count;
mod retries_count;
mod sessionized_metrics;
mod success_rate;

use avg_ticket_size::AvgTicketSize;
use connector_success_rate::ConnectorSuccessRate;
use debit_routing::DebitRouting;
use payment_count::PaymentCount;
use payment_processed_amount::PaymentProcessedAmount;
use payment_success_count::PaymentSuccessCount;
use success_rate::PaymentSuccessRate;

use self::retries_count::RetriesCount;

#[derive(Debug, PartialEq, Eq, serde::Deserialize, Hash)]
pub struct PaymentMetricRow {
    pub currency: Option<DBEnumWrapper<storage_enums::Currency>>,
    pub status: Option<DBEnumWrapper<storage_enums::AttemptStatus>>,
    pub connector: Option<String>,
    pub authentication_type: Option<DBEnumWrapper<storage_enums::AuthenticationType>>,
    pub payment_method: Option<String>,
    pub payment_method_type: Option<String>,
    pub client_source: Option<String>,
    pub client_version: Option<String>,
    pub profile_id: Option<String>,
    pub card_network: Option<String>,
    pub merchant_id: Option<String>,
    pub card_last_4: Option<String>,
    pub card_issuer: Option<String>,
    pub error_reason: Option<String>,
    pub first_attempt: Option<bool>,
    pub total: Option<bigdecimal::BigDecimal>,
    pub count: Option<i64>,
    pub routing_approach: Option<DBEnumWrapper<storage_enums::RoutingApproach>>,
    pub signature_network: Option<String>,
    pub is_issuer_regulated: Option<bool>,
    pub is_debit_routed: Option<bool>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub start_bucket: Option<PrimitiveDateTime>,
    #[serde(with = "common_utils::custom_serde::iso8601::option")]
    pub end_bucket: Option<PrimitiveDateTime>,
}

pub trait PaymentMetricAnalytics: LoadRow<PaymentMetricRow> {}

#[async_trait::async_trait]
pub trait PaymentMetric<T>
where
    T: AnalyticsDataSource + PaymentMetricAnalytics,
{
    async fn load_metrics(
        &self,
        dimensions: &[PaymentDimensions],
        auth: &AuthInfo,
        filters: &PaymentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>>;
}

#[async_trait::async_trait]
impl<T> PaymentMetric<T> for PaymentMetrics
where
    T: AnalyticsDataSource + PaymentMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[PaymentDimensions],
        auth: &AuthInfo,
        filters: &PaymentFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(PaymentMetricsBucketIdentifier, PaymentMetricRow)>> {
        match self {
            Self::PaymentSuccessRate => {
                PaymentSuccessRate
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::PaymentCount => {
                PaymentCount
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::PaymentSuccessCount => {
                PaymentSuccessCount
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::PaymentProcessedAmount => {
                PaymentProcessedAmount
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::AvgTicketSize => {
                AvgTicketSize
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::RetriesCount => {
                RetriesCount
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::ConnectorSuccessRate => {
                ConnectorSuccessRate
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::DebitRouting => {
                DebitRouting
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::SessionizedPaymentSuccessRate => {
                sessionized_metrics::PaymentSuccessRate
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::SessionizedPaymentCount => {
                sessionized_metrics::PaymentCount
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::SessionizedPaymentSuccessCount => {
                sessionized_metrics::PaymentSuccessCount
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::SessionizedPaymentProcessedAmount => {
                sessionized_metrics::PaymentProcessedAmount
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::SessionizedAvgTicketSize => {
                sessionized_metrics::AvgTicketSize
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::SessionizedRetriesCount => {
                sessionized_metrics::RetriesCount
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::SessionizedConnectorSuccessRate => {
                sessionized_metrics::ConnectorSuccessRate
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::PaymentsDistribution => {
                sessionized_metrics::PaymentsDistribution
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::FailureReasons => {
                sessionized_metrics::FailureReasons
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
            Self::SessionizedDebitRouting => {
                sessionized_metrics::DebitRouting
                    .load_metrics(dimensions, auth, filters, granularity, time_range, pool)
                    .await
            }
        }
    }
}


pub struct IatapayPaymentsResponse {
    pub status: IatapayPaymentStatus,
    pub iata_payment_id: Option<String>,
    pub iata_refund_id: Option<String>,
    pub merchant_id: Option<Secret<String>>,
    pub merchant_payment_id: Option<String>,
    pub amount: FloatMajorUnit,
    pub currency: String,
    pub checkout_methods: Option<CheckoutMethod>,
    pub failure_code: Option<String>,
    pub failure_details: Option<String>,
}


File: crates/router/src/core/payments/transformers.rs
 Public functions: 23
 Public structs: 1

use std::{fmt::Debug, marker::PhantomData, str::FromStr};

use api_models::payments::{
    Address, ConnectorMandateReferenceId, CustomerDetails, CustomerDetailsResponse, FrmMessage,
    MandateIds, RequestSurchargeDetails,
};
use common_enums::{Currency, RequestIncrementalAuthorization};
use common_utils::{
    consts::X_HS_LATENCY,
    fp_utils, pii,
    types::{
        self as common_utils_type, AmountConvertor, MinorUnit, StringMajorUnit,
        StringMajorUnitForConnector,
    },
};
use diesel_models::{
    ephemeral_key,
    payment_attempt::ConnectorMandateReferenceId as DieselConnectorMandateReferenceId,
};
use error_stack::{report, ResultExt};
#[cfg(feature = "v2")]
use hyperswitch_domain_models::ApiModelToDieselModelConvertor;
use hyperswitch_domain_models::{payments::payment_intent::CustomerData, router_request_types};
#[cfg(feature = "v2")]
use hyperswitch_interfaces::api::ConnectorSpecifications;
#[cfg(feature = "v2")]
use hyperswitch_interfaces::connector_integration_interface::RouterDataConversion;
#[cfg(feature = "v2")]
use masking::PeekInterface;
use masking::{ExposeInterface, Maskable, Secret};
use router_env::{instrument, tracing};

use super::{flows::Feature, types::AuthenticationData, OperationSessionGetters, PaymentData};
use crate::{
    configs::settings::ConnectorRequestReferenceIdConfig,
    core::{
        errors::{self, RouterResponse, RouterResult},
        payments::{self, helpers},
        utils as core_utils,
    },
    headers::{X_CONNECTOR_HTTP_STATUS_CODE, X_PAYMENT_CONFIRM_SOURCE},
    routes::{metrics, SessionState},
    services::{self, RedirectForm},
    types::{
        self,
        api::{self, ConnectorTransactionId},
        domain, payment_methods as pm_types,
        storage::{self, enums},
        transformers::{ForeignFrom, ForeignInto, ForeignTryFrom},
        MultipleCaptureRequestData,
    },
    utils::{OptionExt, ValueExt},
};

#[cfg(feature = "v2")]
pub async fn construct_router_data_to_update_calculated_tax<'a, F, T>(
    state: &'a SessionState,
    payment_data: PaymentData<F>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &helpers::MerchantConnectorAccountType,
) -> RouterResult<types::RouterData<F, T, types::PaymentsResponseData>>
where
    T: TryFrom<PaymentAdditionalData<'a, F>>,
    types::RouterData<F, T, types::PaymentsResponseData>: Feature<F, T>,
    F: Clone,
    error_stack::Report<errors::ApiErrorResponse>:
        From<<T as TryFrom<PaymentAdditionalData<'a, F>>>::Error>,
{
    todo!()
}

#[cfg(feature = "v1")]
pub async fn construct_router_data_to_update_calculated_tax<'a, F, T>(
    state: &'a SessionState,
    payment_data: PaymentData<F>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &helpers::MerchantConnectorAccountType,
) -> RouterResult<types::RouterData<F, T, types::PaymentsResponseData>>
where
    T: TryFrom<PaymentAdditionalData<'a, F>>,
    types::RouterData<F, T, types::PaymentsResponseData>: Feature<F, T>,
    F: Clone,
    error_stack::Report<errors::ApiErrorResponse>:
        From<<T as TryFrom<PaymentAdditionalData<'a, F>>>::Error>,
{
    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    let test_mode = merchant_connector_account.is_test_mode_on();

    let auth_type: types::ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .parse_value("ConnectorAuthType")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    let additional_data = PaymentAdditionalData {
        router_base_url: state.base_url.clone(),
        connector_name: connector_id.to_string(),
        payment_data: payment_data.clone(),
        state,
        customer_data: customer,
    };

    let connector_mandate_request_reference_id = payment_data
        .payment_attempt
        .connector_mandate_detail
        .as_ref()
        .and_then(|detail| detail.get_connector_mandate_request_reference_id());

    let router_data = types::RouterData {
        flow: PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        customer_id: None,
        connector: connector_id.to_owned(),
        payment_id: payment_data
            .payment_attempt
            .payment_id
            .get_string_repr()
            .to_owned(),
        tenant_id: state.tenant.tenant_id.clone(),
        attempt_id: payment_data.payment_attempt.get_id().to_owned(),
        status: payment_data.payment_attempt.status,
        payment_method: diesel_models::enums::PaymentMethod::default(),
        connector_auth_type: auth_type,
        description: None,
        address: payment_data.address.clone(),
        auth_type: payment_data
            .payment_attempt
            .authentication_type
            .unwrap_or_default(),
        connector_meta_data: None,
        connector_wallets_details: None,
        request: T::try_from(additional_data)?,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_status: None,
        payment_method_token: None,
        connector_customer: None,
        recurring_mandate_payment_data: None,
        connector_request_reference_id: core_utils::get_connector_request_reference_id(
            &state.conf,
            merchant_context.get_merchant_account().get_id(),
            &payment_data.payment_intent,
            &payment_data.payment_attempt,
            connector_id,
        )?,
        preprocessing_id: None,
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        test_mode,
        payment_method_balance: None,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload: None,
        connector_mandate_request_reference_id,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: payment_data.payment_intent.is_payment_id_from_merchant,
        l2_l3_data: None,
        minor_amount_capturable: None,
    };
    Ok(router_data)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_external_vault_proxy_router_data_v2<'a>(
    state: &'a SessionState,
    merchant_account: &domain::MerchantAccount,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    payment_data: &hyperswitch_domain_models::payments::PaymentConfirmData<api::ExternalVaultProxy>,
    request: types::ExternalVaultProxyPaymentsData,
    connector_request_reference_id: String,
    connector_customer_id: Option<String>,
    customer_id: Option<common_utils::id_type::CustomerId>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<
    hyperswitch_domain_models::router_data_v2::RouterDataV2<
        api::ExternalVaultProxy,
        hyperswitch_domain_models::router_data_v2::ExternalVaultProxyFlowData,
        types::ExternalVaultProxyPaymentsData,
        types::PaymentsResponseData,
    >,
> {
    use hyperswitch_domain_models::router_data_v2::{ExternalVaultProxyFlowData, RouterDataV2};

    let auth_type = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    let external_vault_proxy_flow_data = ExternalVaultProxyFlowData {
        merchant_id: merchant_account.get_id().clone(),
        customer_id,
        connector_customer: connector_customer_id,
        payment_id: payment_data
            .payment_attempt
            .payment_id
            .get_string_repr()
            .to_owned(),
        attempt_id: payment_data
            .payment_attempt
            .get_id()
            .get_string_repr()
            .to_owned(),
        status: payment_data.payment_attempt.status,
        payment_method: payment_data.payment_attempt.payment_method_type,
        description: payment_data
            .payment_intent
            .description
            .as_ref()
            .map(|description| description.get_string_repr())
            .map(ToOwned::to_owned),
        address: payment_data.payment_address.clone(),
        auth_type: payment_data.payment_attempt.authentication_type,
        connector_meta_data: merchant_connector_account.get_metadata(),
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_token: None,
        recurring_mandate_payment_data: None,
        preprocessing_id: payment_data.payment_attempt.preprocessing_step_id.clone(),
        payment_method_balance: None,
        connector_api_version: None,
        connector_request_reference_id,
        test_mode: Some(true),
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        connector_response: None,
        payment_method_status: None,
    };

    let router_data_v2 = RouterDataV2 {
        flow: PhantomData,
        tenant_id: state.tenant.tenant_id.clone(),
        resource_common_data: external_vault_proxy_flow_data,
        connector_auth_type: auth_type,
        request,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
    };

    Ok(router_data_v2)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_payment_router_data_for_authorize<'a>(
    state: &'a SessionState,
    payment_data: hyperswitch_domain_models::payments::PaymentConfirmData<api::Authorize>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    _merchant_recipient_data: Option<types::MerchantRecipientData>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<types::PaymentsAuthorizeRouterData> {
    use masking::ExposeOptionInterface;

    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    let auth_type = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    // TODO: Take Globalid and convert to connector reference id
    let customer_id = customer
        .to_owned()
        .map(|customer| common_utils::id_type::CustomerId::try_from(customer.id.clone()))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Invalid global customer generated, not able to convert to reference id",
        )?;

    let connector_customer_id =
        payment_data.get_connector_customer_id(customer.as_ref(), merchant_connector_account);

    let payment_method = payment_data.payment_attempt.payment_method_type;

    let router_base_url = &state.base_url;
    let attempt = &payment_data.payment_attempt;

    let complete_authorize_url = Some(helpers::create_complete_authorize_url(
        router_base_url,
        attempt,
        connector_id,
        None,
    ));

    let webhook_url = match merchant_connector_account {
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(
            merchant_connector_account,
        ) => Some(helpers::create_webhook_url(
            router_base_url,
            &attempt.merchant_id,
            merchant_connector_account.get_id().get_string_repr(),
        )),
        // TODO: Implement for connectors that require a webhook URL to be included in the request payload.
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None,
    };

    let router_return_url = payment_data
        .payment_intent
        .create_finish_redirection_url(
            router_base_url,
            merchant_context
                .get_merchant_account()
                .publishable_key
                .as_ref(),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to construct finish redirection url")?
        .to_string();

    let connector_request_reference_id = payment_data
        .payment_attempt
        .connector_request_reference_id
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("connector_request_reference_id not found in payment_attempt")?;

    let email = customer
        .as_ref()
        .and_then(|customer| customer.email.clone())
        .map(pii::Email::from);

    let browser_info = payment_data
        .payment_attempt
        .browser_info
        .clone()
        .map(types::BrowserInformation::from);
    let additional_payment_method_data: Option<api_models::payments::AdditionalPaymentData> =
            payment_data.payment_attempt
                .payment_method_data
                .as_ref().map(|data| data.clone().parse_value("AdditionalPaymentData"))
                .transpose()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to parse AdditionalPaymentData from payment_data.payment_attempt.payment_method_data")?;

    // TODO: few fields are repeated in both routerdata and request
    let request = types::PaymentsAuthorizeData {
        payment_method_data: payment_data
            .payment_method_data
            .get_required_value("payment_method_data")?,
        setup_future_usage: Some(payment_data.payment_intent.setup_future_usage),
        mandate_id: payment_data.mandate_data.clone(),
        off_session: None,
        setup_mandate_details: None,
        confirm: true,
        statement_descriptor_suffix: None,
        statement_descriptor: None,
        capture_method: Some(payment_data.payment_intent.capture_method),
        amount: payment_data
            .payment_attempt
            .amount_details
            .get_net_amount()
            .get_amount_as_i64(),
        minor_amount: payment_data.payment_attempt.amount_details.get_net_amount(),
        order_tax_amount: None,
        currency: payment_data.payment_intent.amount_details.currency,
        browser_info,
        email,
        customer_name: None,
        payment_experience: None,
        order_details: None,
        order_category: None,
        session_token: None,
        enrolled_for_3ds: true,
        related_transaction_id: None,
        payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype),
        router_return_url: Some(router_return_url),
        webhook_url,
        complete_authorize_url,
        customer_id: customer_id.clone(),
        surcharge_details: None,
        request_extended_authorization: None,
        request_incremental_authorization: matches!(
            payment_data
                .payment_intent
                .request_incremental_authorization,
            RequestIncrementalAuthorization::True
        ),
        metadata: payment_data.payment_intent.metadata.expose_option(),
        authentication_data: None,
        customer_acceptance: None,
        split_payments: None,
        merchant_order_reference_id: payment_data
            .payment_intent
            .merchant_reference_id
            .map(|reference_id| reference_id.get_string_repr().to_owned()),
        integrity_object: None,
        shipping_cost: payment_data.payment_intent.amount_details.shipping_cost,
        additional_payment_method_data,
        merchant_account_id: None,
        merchant_config_currency: None,
        connector_testing_data: None,
        order_id: None,
        locale: None,
        payment_channel: None,
        enable_partial_authorization: None,
    };
    let connector_mandate_request_reference_id = payment_data
        .payment_attempt
        .connector_token_details
        .as_ref()
        .and_then(|detail| detail.get_connector_token_request_reference_id());

    // TODO: evaluate the fields in router data, if they are required or not
    let router_data = types::RouterData {
        flow: PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        tenant_id: state.tenant.tenant_id.clone(),
        // TODO: evaluate why we need customer id at the connector level. We already have connector customer id.
        customer_id,
        connector: connector_id.to_owned(),
        // TODO: evaluate why we need payment id at the connector level. We already have connector reference id
        payment_id: payment_data
            .payment_attempt
            .payment_id
            .get_string_repr()
            .to_owned(),
        // TODO: evaluate why we need attempt id at the connector level. We already have connector reference id
        attempt_id: payment_data
            .payment_attempt
            .get_id()
            .get_string_repr()
            .to_owned(),
        status: payment_data.payment_attempt.status,
        payment_method,
        connector_auth_type: auth_type,
        description: payment_data
            .payment_intent
            .description
            .as_ref()
            .map(|description| description.get_string_repr())
            .map(ToOwned::to_owned),
        // TODO: Create unified address
        address: payment_data.payment_address.clone(),
        auth_type: payment_data.payment_attempt.authentication_type,
        connector_meta_data: merchant_connector_account.get_metadata(),
        connector_wallets_details: None,
        request,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_status: None,
        payment_method_token: None,
        connector_customer: connector_customer_id,
        recurring_mandate_payment_data: None,
        // TODO: This has to be generated as the reference id based on the connector configuration
        // Some connectros might not accept accept the global id. This has to be done when generating the reference id
        connector_request_reference_id,
        preprocessing_id: payment_data.payment_attempt.preprocessing_step_id,
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        // TODO: take this based on the env
        test_mode: Some(true),
        payment_method_balance: None,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload,
        connector_mandate_request_reference_id,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: payment_data.payment_intent.is_payment_id_from_merchant,
        l2_l3_data: None,
        minor_amount_capturable: None,
    };

    Ok(router_data)
}
#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_external_vault_proxy_payment_router_data<'a>(
    state: &'a SessionState,
    payment_data: hyperswitch_domain_models::payments::PaymentConfirmData<api::ExternalVaultProxy>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    _merchant_recipient_data: Option<types::MerchantRecipientData>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<types::ExternalVaultProxyPaymentsRouterData> {
    use masking::ExposeOptionInterface;

    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    let auth_type = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    // TODO: Take Globalid and convert to connector reference id
    let customer_id = customer
        .to_owned()
        .map(|customer| common_utils::id_type::CustomerId::try_from(customer.id.clone()))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Invalid global customer generated, not able to convert to reference id",
        )?;

    let connector_customer_id =
        payment_data.get_connector_customer_id(customer.as_ref(), merchant_connector_account);

    let payment_method = payment_data.payment_attempt.payment_method_type;

    let router_base_url = &state.base_url;
    let attempt = &payment_data.payment_attempt;

    let complete_authorize_url = Some(helpers::create_complete_authorize_url(
        router_base_url,
        attempt,
        connector_id,
        None,
    ));

    let webhook_url = match merchant_connector_account {
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(
            merchant_connector_account,
        ) => Some(helpers::create_webhook_url(
            router_base_url,
            &attempt.merchant_id,
            merchant_connector_account.get_id().get_string_repr(),
        )),
        // TODO: Implement for connectors that require a webhook URL to be included in the request payload.
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None,
    };

    let router_return_url = payment_data
        .payment_intent
        .create_finish_redirection_url(
            router_base_url,
            merchant_context
                .get_merchant_account()
                .publishable_key
                .as_ref(),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to construct finish redirection url")?
        .to_string();

    let connector_request_reference_id = payment_data
        .payment_attempt
        .connector_request_reference_id
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("connector_request_reference_id not found in payment_attempt")?;

    let email = customer
        .as_ref()
        .and_then(|customer| customer.email.clone())
        .map(pii::Email::from);

    let browser_info = payment_data
        .payment_attempt
        .browser_info
        .clone()
        .map(types::BrowserInformation::from);
    // TODO: few fields are repeated in both routerdata and request
    let request = types::ExternalVaultProxyPaymentsData {
        payment_method_data: payment_data
            .external_vault_pmd
            .clone()
            .get_required_value("external vault proxy payment_method_data")?,
        setup_future_usage: Some(payment_data.payment_intent.setup_future_usage),
        mandate_id: payment_data.mandate_data.clone(),
        off_session: None,
        setup_mandate_details: None,
        confirm: true,
        statement_descriptor_suffix: None,
        statement_descriptor: None,
        capture_method: Some(payment_data.payment_intent.capture_method),
        amount: payment_data
            .payment_attempt
            .amount_details
            .get_net_amount()
            .get_amount_as_i64(),
        minor_amount: payment_data.payment_attempt.amount_details.get_net_amount(),
        order_tax_amount: None,
        currency: payment_data.payment_intent.amount_details.currency,
        browser_info,
        email,
        customer_name: None,
        payment_experience: None,
        order_details: None,
        order_category: None,
        session_token: None,
        enrolled_for_3ds: true,
        related_transaction_id: None,
        payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype),
        router_return_url: Some(router_return_url),
        webhook_url,
        complete_authorize_url,
        customer_id: customer_id.clone(),
        surcharge_details: None,
        request_extended_authorization: None,
        request_incremental_authorization: matches!(
            payment_data
                .payment_intent
                .request_incremental_authorization,
            RequestIncrementalAuthorization::True
        ),
        metadata: payment_data.payment_intent.metadata.clone().expose_option(),
        authentication_data: None,
        customer_acceptance: None,
        split_payments: None,
        merchant_order_reference_id: None,
        integrity_object: None,
        shipping_cost: payment_data.payment_intent.amount_details.shipping_cost,
        additional_payment_method_data: None,
        merchant_account_id: None,
        merchant_config_currency: None,
        connector_testing_data: None,
        order_id: None,
    };
    let connector_mandate_request_reference_id = payment_data
        .payment_attempt
        .connector_token_details
        .as_ref()
        .and_then(|detail| detail.get_connector_token_request_reference_id());

    // Construct RouterDataV2 for external vault proxy
    let router_data_v2 = construct_external_vault_proxy_router_data_v2(
        state,
        merchant_context.get_merchant_account(),
        merchant_connector_account,
        &payment_data,
        request,
        connector_request_reference_id.clone(),
        connector_customer_id.clone(),
        customer_id.clone(),
        header_payload.clone(),
    )
    .await?;

    // Convert RouterDataV2 to old RouterData (v1) using the existing RouterDataConversion trait
    let router_data = hyperswitch_domain_models::router_data_v2::flow_common_types::ExternalVaultProxyFlowData::to_old_router_data(router_data_v2)
    .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Cannot construct router data for making the unified connector service call",
        )?;

    Ok(router_data)
}
#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_payment_router_data_for_capture<'a>(
    state: &'a SessionState,
    payment_data: hyperswitch_domain_models::payments::PaymentCaptureData<api::Capture>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    _merchant_recipient_data: Option<types::MerchantRecipientData>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<types::PaymentsCaptureRouterData> {
    use masking::ExposeOptionInterface;

    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    let auth_type = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    let customer_id = customer
        .to_owned()
        .map(|customer| common_utils::id_type::CustomerId::try_from(customer.id.clone()))
        .transpose()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable(
            "Invalid global customer generated, not able to convert to reference id",
        )?;

    let payment_method = payment_data.payment_attempt.payment_method_type;

    let connector_mandate_request_reference_id = payment_data
        .payment_attempt
        .connector_token_details
        .as_ref()
        .and_then(|detail| detail.get_connector_token_request_reference_id());

    let connector = api::ConnectorData::get_connector_by_name(
        &state.conf.connectors,
        connector_id,
        api::GetToken::Connector,
        payment_data.payment_attempt.merchant_connector_id.clone(),
    )?;

    let connector_request_reference_id = payment_data
        .payment_attempt
        .connector_request_reference_id
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("connector_request_reference_id not found in payment_attempt")?;

    let amount_to_capture = payment_data
        .payment_attempt
        .amount_details
        .get_amount_to_capture()
        .unwrap_or(payment_data.payment_attempt.amount_details.get_net_amount());

    let amount = payment_data.payment_attempt.amount_details.get_net_amount();
    let request = types::PaymentsCaptureData {
        capture_method: Some(payment_data.payment_intent.capture_method),
        amount_to_capture: amount_to_capture.get_amount_as_i64(), // This should be removed once we start moving to connector module
        minor_amount_to_capture: amount_to_capture,
        currency: payment_data.payment_intent.amount_details.currency,
        connector_transaction_id: connector
            .connector
            .connector_transaction_id(&payment_data.payment_attempt)?
            .ok_or(errors::ApiErrorResponse::ResourceIdNotFound)?,
        payment_amount: amount.get_amount_as_i64(), // This should be removed once we start moving to connector module
        minor_payment_amount: amount,
        connector_meta: payment_data
            .payment_attempt
            .connector_metadata
            .clone()
            .expose_option(),
        // TODO: add multiple capture data
        multiple_capture_data: None,
        // TODO: why do we need browser info during capture?
        browser_info: None,
        metadata: payment_data.payment_intent.metadata.expose_option(),
        integrity_object: None,
        split_payments: None,
        webhook_url: None,
    };

    // TODO: evaluate the fields in router data, if they are required or not
    let router_data = types::RouterData {
        flow: PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        // TODO: evaluate why we need customer id at the connector level. We already have connector customer id.
        customer_id,
        connector: connector_id.to_owned(),
        tenant_id: state.tenant.tenant_id.clone(),
        // TODO: evaluate why we need payment id at the connector level. We already have connector reference id
        payment_id: payment_data
            .payment_attempt
            .payment_id
            .get_string_repr()
            .to_owned(),
        // TODO: evaluate why we need attempt id at the connector level. We already have connector reference id
        attempt_id: payment_data
            .payment_attempt
            .get_id()
            .get_string_repr()
            .to_owned(),
        status: payment_data.payment_attempt.status,
        payment_method,
        connector_auth_type: auth_type,
        description: payment_data
            .payment_intent
            .description
            .as_ref()
            .map(|description| description.get_string_repr())
            .map(ToOwned::to_owned),
        // TODO: Create unified address
        address: hyperswitch_domain_models::payment_address::PaymentAddress::default(),
        auth_type: payment_data.payment_attempt.authentication_type,
        connector_meta_data: None,
        connector_wallets_details: None,
        request,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_status: None,
        payment_method_token: None,
        connector_customer: None,
        recurring_mandate_payment_data: None,
        // TODO: This has to be generated as the reference id based on the connector configuration
        // Some connectros might not accept accept the global id. This has to be done when generating the reference id
        connector_request_reference_id,
        preprocessing_id: payment_data.payment_attempt.preprocessing_step_id,
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        // TODO: take this based on the env
        test_mode: Some(true),
        payment_method_balance: None,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload,
        connector_mandate_request_reference_id,
        psd2_sca_exemption_type: None,
        authentication_id: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: None,
        l2_l3_data: None,
        minor_amount_capturable: None,
    };

    Ok(router_data)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_router_data_for_psync<'a>(
    state: &'a SessionState,
    payment_data: hyperswitch_domain_models::payments::PaymentStatusData<api::PSync>,
    connector_id: &str,
    merchant_context: &domain::MerchantContext,
    customer: &'a Option<domain::Customer>,
    merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
    _merchant_recipient_data: Option<types::MerchantRecipientData>,
    header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
) -> RouterResult<types::PaymentsSyncRouterData> {
    use masking::ExposeOptionInterface;

    fp_utils::when(merchant_connector_account.is_disabled(), || {
        Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled)
    })?;

    // TODO: Take Globalid / CustomerReferenceId and convert to connector reference id
    let customer_id = None;

    let payment_intent = payment_data.payment_intent;

    let auth_type: types::ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while parsing value for ConnectorAuthType")?;

    let attempt = &payment_data.payment_attempt;

    let connector_request_reference_id = payment_data
        .payment_attempt
        .connector_request_reference_id
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("connector_request_reference_id not found in payment_attempt")?;

    let request = types::PaymentsSyncData {
        amount: attempt.amount_details.get_net_amount(),
        integrity_object: None,
        mandate_id: None,
        connector_transaction_id: match attempt.get_connector_payment_id() {
            Some(connector_txn_id) => {
                types::ResponseId::ConnectorTransactionId(connector_txn_id.to_owned())
            }
            None => types::ResponseId::NoResponseId,
        },
        encoded_data: attempt.encoded_data.clone().expose_option(),
        capture_method: Some(payment_intent.capture_method),
        connector_meta: attempt.connector_metadata.clone().expose_option(),
        sync_type: types::SyncRequestType::SinglePaymentSync,
        payment_method_type: Some(attempt.payment_method_subtype),
        currency: payment_intent.amount_details.currency,
        // TODO: Get the charges object from feature metadata
        split_payments: None,
        payment_experience: None,
        connector_reference_id: attempt.connector_response_reference_id.clone(),
    };

    // TODO: evaluate the fields in router data, if they are required or not
    let router_data = types::RouterData {
        flow: PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        // TODO: evaluate why we need customer id at the connector level. We already have connector customer id.
        customer_id,
        tenant_id: state.tenant.tenant_id.clone(),
        connector: connector_id.to_owned(),
        // TODO: evaluate why we need payment id at the connector level. We already have connector reference id
        payment_id: payment_intent.id.get_string_repr().to_owned(),
        // TODO: evaluate why we need attempt id at the connector level. We already have connector reference id
        attempt_id: attempt.get_id().get_string_repr().to_owned(),
        status: attempt.status,
        payment_method: attempt.payment_method_type,
        connector_auth_type: auth_type,
        description: payment_intent
            .description
            .as_ref()
            .map(|description| description.get_string_repr())
            .map(ToOwned::to_owned),
        // TODO: Create unified address
        address: hyperswitch_domain_models::payment_address::PaymentAddress::default(),
        auth_type: attempt.authentication_type,
        connector_meta_data: None,
        connector_wallets_details: None,
        request,
        response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()),
        amount_captured: None,
        minor_amount_captured: None,
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_status: None,
        payment_method_token: None,
        connector_customer: None,
        recurring_mandate_payment_data: None,
        // TODO: This has to be generated as the reference id based on the connector configuration
        // Some connectros might not accept accept the global id. This has to be done when generating the reference id
        connector_request_reference_id,
        preprocessing_id: attempt.preprocessing_step_id.clone(),
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        // TODO: take this based on the env
        test_mode: Some(true),
        payment_method_balance: None,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload,
        connector_mandate_request_reference_id: None,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: None,
        l2_l3_data: None,
        minor_amount_capturable: None,
    };

    Ok(router_data)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
pub async fn construct_payment_router_data_for_sdk_session<'a>(


pub struct MandateAmountData {
    pub amount: MinorUnit,
    pub currency: Currency,
    pub start_date: Option<PrimitiveDateTime>,
    pub end_date: Option<PrimitiveDateTime>,
    pub metadata: Option<pii::SecretSerdeValue>,
}


pub struct RazorpaySyncResponse {
    items: Vec<RazorpaySyncItem>,
}


File: crates/router/src/core/fraud_check/flows/fulfillment_flow.rs
 Public functions: 2

use common_utils::ext_traits::{OptionExt, ValueExt};
use error_stack::ResultExt;
use router_env::tracing::{self, instrument};

use crate::{
    core::{
        errors::RouterResult, fraud_check::frm_core_types::FrmFulfillmentRequest,
        payments::helpers, utils as core_utils,
    },
    errors,
    types::{
        domain,
        fraud_check::{FraudCheckFulfillmentData, FrmFulfillmentRouterData},
        storage, ConnectorAuthType, ErrorResponse, PaymentAddress, RouterData,
    },
    utils, SessionState,
};

#[cfg(feature = "v2")]
pub async fn construct_fulfillment_router_data<'a>(
    _state: &'a SessionState,
    _payment_intent: &'a storage::PaymentIntent,
    _payment_attempt: &storage::PaymentAttempt,
    _merchant_context: &domain::MerchantContext,
    _connector: String,
    _fulfillment_request: FrmFulfillmentRequest,
) -> RouterResult<FrmFulfillmentRouterData> {
    todo!()
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn construct_fulfillment_router_data<'a>(
    state: &'a SessionState,
    payment_intent: &'a storage::PaymentIntent,
    payment_attempt: &storage::PaymentAttempt,
    merchant_context: &domain::MerchantContext,
    connector: String,
    fulfillment_request: FrmFulfillmentRequest,
) -> RouterResult<FrmFulfillmentRouterData> {
    let profile_id = payment_intent
        .profile_id
        .as_ref()
        .get_required_value("profile_id")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("profile_id is not set in payment_intent")?
        .clone();

    let connector_id = connector.clone();

    let merchant_connector_account = helpers::get_merchant_connector_account(
        state,
        merchant_context.get_merchant_account().get_id(),
        None,
        merchant_context.get_merchant_key_store(),
        &profile_id,
        &connector,
        None,
    )
    .await?;

    let test_mode: Option<bool> = merchant_connector_account.is_test_mode_on();
    let auth_type: ConnectorAuthType = merchant_connector_account
        .get_connector_account_details()
        .parse_value("ConnectorAuthType")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    let payment_method = utils::OptionExt::get_required_value(
        payment_attempt.payment_method,
        "payment_method_type",
    )?;
    let router_data = RouterData {
        flow: std::marker::PhantomData,
        merchant_id: merchant_context.get_merchant_account().get_id().clone(),
        tenant_id: state.tenant.tenant_id.clone(),
        connector,
        payment_id: payment_attempt.payment_id.get_string_repr().to_owned(),
        attempt_id: payment_attempt.attempt_id.clone(),
        status: payment_attempt.status,
        payment_method,
        connector_auth_type: auth_type,
        description: None,
        address: PaymentAddress::default(),
        auth_type: payment_attempt.authentication_type.unwrap_or_default(),
        connector_meta_data: merchant_connector_account.get_metadata(),
        connector_wallets_details: merchant_connector_account.get_connector_wallets_details(),
        amount_captured: payment_intent
            .amount_captured
            .map(|amt| amt.get_amount_as_i64()),
        minor_amount_captured: payment_intent.amount_captured,
        payment_method_status: None,
        request: FraudCheckFulfillmentData {
            amount: payment_attempt
                .net_amount
                .get_total_amount()
                .get_amount_as_i64(),
            order_details: payment_intent.order_details.clone(),
            fulfillment_req: fulfillment_request,
        },
        response: Err(ErrorResponse::default()),
        access_token: None,
        session_token: None,
        reference_id: None,
        payment_method_token: None,
        connector_customer: None,
        customer_id: None,
        recurring_mandate_payment_data: None,
        preprocessing_id: None,
        payment_method_balance: None,
        connector_request_reference_id: core_utils::get_connector_request_reference_id(
            &state.conf,
            merchant_context.get_merchant_account().get_id(),
            payment_intent,
            payment_attempt,
            &connector_id,
        )?,
        #[cfg(feature = "payouts")]
        payout_method_data: None,
        #[cfg(feature = "payouts")]
        quote_id: None,
        test_mode,
        connector_api_version: None,
        connector_http_status_code: None,
        external_latency: None,
        apple_pay_flow: None,
        frm_metadata: None,
        refund_id: None,
        dispute_id: None,
        connector_response: None,
        integrity_check: Ok(()),
        additional_merchant_data: None,
        header_payload: None,
        connector_mandate_request_reference_id: None,
        authentication_id: None,
        psd2_sca_exemption_type: None,
        raw_connector_response: None,
        is_payment_id_from_merchant: None,
        l2_l3_data: None,
        minor_amount_capturable: None,
    };
    Ok(router_data)
}


pub struct RefusedResponse {
    pub refusal_description: String,
    // Access Worldpay returns a raw response code in the refusalCode field (if enabled) containing the unmodified response code received either directly from the card scheme for Worldpay-acquired transactions, or from third party acquirers.
    pub refusal_code: String,
    pub risk_factors: Option<Vec<RiskFactorsInner>>,
    pub fraud: Option<Fraud>,
    #[serde(rename = "threeDS")]
    pub three_ds: Option<ThreeDsResponse>,
    pub advice: Option<Advice>,
}