File: crates/diesel_models/src/events.rs
 Public structs: 3

use common_utils::{custom_serde, encryption::Encryption};
use diesel::{
    expression::AsExpression, AsChangeset, Identifiable, Insertable, Queryable, Selectable,
};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{enums as storage_enums, schema::events};

#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = events)]
pub struct EventNew {
    pub event_id: String,
    pub event_type: storage_enums::EventType,
    pub event_class: storage_enums::EventClass,
    pub is_webhook_notified: bool,
    pub primary_object_id: String,
    pub primary_object_type: storage_enums::EventObjectType,
    pub created_at: PrimitiveDateTime,
    pub merchant_id: Option<common_utils::id_type::MerchantId>,
    pub business_profile_id: Option<common_utils::id_type::ProfileId>,
    pub primary_object_created_at: Option<PrimitiveDateTime>,
    pub idempotent_event_id: Option<String>,
    pub initial_attempt_id: Option<String>,
    pub request: Option<Encryption>,
    pub response: Option<Encryption>,
    pub delivery_attempt: Option<storage_enums::WebhookDeliveryAttempt>,
    pub metadata: Option<EventMetadata>,
    pub is_overall_delivery_successful: Option<bool>,
}

#[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = events)]
pub struct EventUpdateInternal {
    pub is_webhook_notified: Option<bool>,
    pub response: Option<Encryption>,
    pub is_overall_delivery_successful: Option<bool>,
}

#[derive(Clone, Debug, Deserialize, Serialize, Identifiable, Queryable, Selectable)]
#[diesel(table_name = events, primary_key(event_id), check_for_backend(diesel::pg::Pg))]
pub struct Event {
    pub event_id: String,
    pub event_type: storage_enums::EventType,
    pub event_class: storage_enums::EventClass,
    pub is_webhook_notified: bool,
    pub primary_object_id: String,
    pub primary_object_type: storage_enums::EventObjectType,
    #[serde(with = "custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    pub merchant_id: Option<common_utils::id_type::MerchantId>,
    pub business_profile_id: Option<common_utils::id_type::ProfileId>,
    // This column can be used to partition the database table, so that all events related to a
    // single object would reside in the same partition
    pub primary_object_created_at: Option<PrimitiveDateTime>,
    pub idempotent_event_id: Option<String>,
    pub initial_attempt_id: Option<String>,
    pub request: Option<Encryption>,
    pub response: Option<Encryption>,
    pub delivery_attempt: Option<storage_enums::WebhookDeliveryAttempt>,
    pub metadata: Option<EventMetadata>,
    pub is_overall_delivery_successful: Option<bool>,
}

#[derive(Clone, Debug, Deserialize, Serialize, AsExpression, diesel::FromSqlRow)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub enum EventMetadata {
    #[cfg(feature = "v1")]
    Payment {
        payment_id: common_utils::id_type::PaymentId,
    },
    #[cfg(feature = "v2")]
    Payment {
        payment_id: common_utils::id_type::GlobalPaymentId,
    },
    Payout {
        payout_id: common_utils::id_type::PayoutId,
    },
    #[cfg(feature = "v1")]
    Refund {
        payment_id: common_utils::id_type::PaymentId,
        refund_id: String,
    },
    #[cfg(feature = "v2")]
    Refund {
        payment_id: common_utils::id_type::GlobalPaymentId,
        refund_id: common_utils::id_type::GlobalRefundId,
    },
    #[cfg(feature = "v1")]
    Dispute {
        payment_id: common_utils::id_type::PaymentId,
        attempt_id: String,
        dispute_id: String,
    },
    #[cfg(feature = "v2")]
    Dispute {
        payment_id: common_utils::id_type::GlobalPaymentId,
        attempt_id: String,
        dispute_id: String,
    },
    Mandate {
        payment_method_id: String,
        mandate_id: String,
    },
}

common_utils::impl_to_sql_from_sql_json!(EventMetadata);


impl api::PaymentAuthorize for Bluesnap {}

impl api::PaymentAuthorize for for Bluesnap


impl MerchantConnectorAccount {
    pub fn get_id(&self) -> id_type::MerchantConnectorAccountId {
        self.id.clone()
    }
}


impl IncomingWebhook for Itaubank {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

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

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


impl api::Refund for Worldpayxml {}


File: crates/hyperswitch_connectors/src/connectors/flexiti/transformers.rs
 Public structs: 13

use common_enums::enums;
use common_utils::{pii::Email, request::Method, types::FloatMajorUnit};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{AccessToken, ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::{
    types::{self, RefundsResponseRouterData, ResponseRouterData},
    utils::{
        self, AddressDetailsData, BrowserInformationData, PaymentsAuthorizeRequestData,
        RouterData as _,
    },
};

pub struct FlexitiRouterData<T> {
    pub amount: FloatMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

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

#[derive(Debug, Serialize)]
pub struct FlexitiPaymentsRequest {
    merchant_order_id: Option<String>,
    lang: String,
    flow: FlexitiFlow,
    amount_requested: FloatMajorUnit,
    email: Option<Email>,
    fname: Secret<String>,
    billing_information: BillingInformation,
    shipping_information: ShippingInformation,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FlexitiFlow {
    #[serde(rename = "apply/buy")]
    ApplyAndBuy,
    Apply,
    Buy,
}
#[derive(Debug, Serialize)]
pub struct BillingInformation {
    first_name: Secret<String>,
    last_name: Secret<String>,
    address_1: Secret<String>,
    address_2: Secret<String>,
    city: Secret<String>,
    postal_code: Secret<String>,
    province: Secret<String>,
}

#[derive(Debug, Serialize)]
pub struct ShippingInformation {
    first_name: Secret<String>,
    last_name: Secret<String>,
    address_1: Secret<String>,
    address_2: Secret<String>,
    city: Secret<String>,
    postal_code: Secret<String>,
    province: Secret<String>,
}

impl TryFrom<&FlexitiRouterData<&PaymentsAuthorizeRouterData>> for FlexitiPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &FlexitiRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::PayLater(pay_later_data) => match pay_later_data {
                hyperswitch_domain_models::payment_method_data::PayLaterData::FlexitiRedirect {  } =>  {
                    let shipping_address = item.router_data.get_shipping_address()?;
                    let shipping_information = ShippingInformation {
                        first_name: shipping_address.get_first_name()?.to_owned(),
                        last_name: shipping_address.get_last_name()?.to_owned(),
                        address_1: shipping_address.get_line1()?.to_owned(),
                        address_2: shipping_address.get_line2()?.to_owned(),
                        city: shipping_address.get_city()?.to_owned().into(),
                        postal_code: shipping_address.get_zip()?.to_owned(),
                        province: shipping_address.to_state_code()?,
                    };
                    let billing_information = BillingInformation {
                        first_name: item.router_data.get_billing_first_name()?,
                        last_name: item.router_data.get_billing_last_name()?,
                        address_1: item.router_data.get_billing_line1()?,
                        address_2: item.router_data.get_billing_line2()?,
                        city: item.router_data.get_billing_city()?.into(),
                        postal_code: item.router_data.get_billing_zip()?,
                        province: item.router_data.get_billing_state_code()?,
                    };
                    Ok(Self {
                        merchant_order_id: item.router_data.request.merchant_order_reference_id.to_owned(),
                        lang: item.router_data.request.get_browser_info()?.get_language()?,
                        flow: FlexitiFlow::ApplyAndBuy,
                        amount_requested: item.amount.to_owned(),
                        email: item.router_data.get_optional_billing_email(),
                        fname: item.router_data.get_billing_first_name()?,
                        billing_information,
                        shipping_information,
                    })
                },
                hyperswitch_domain_models::payment_method_data::PayLaterData::KlarnaRedirect {  } |
                hyperswitch_domain_models::payment_method_data::PayLaterData::KlarnaSdk { .. } |
                hyperswitch_domain_models::payment_method_data::PayLaterData::AffirmRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::BreadpayRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::AfterpayClearpayRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::PayBrightRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::WalleyRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::AlmaRedirect {  }  |
                hyperswitch_domain_models::payment_method_data::PayLaterData::AtomeRedirect {  } => {
                                Err(errors::ConnectorError::NotImplemented(
                                utils::get_unimplemented_payment_method_error_message("flexiti"),
                            ))
                            }?,
            }
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

#[derive(Debug, Serialize)]
pub struct FlexitiAccessTokenRequest {
    client_id: Secret<String>,
    client_secret: Secret<String>,
    grant_type: FlexitiGranttype,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FlexitiGranttype {
    Password,
    RefreshToken,
    ClientCredentials,
}

impl TryFrom<&types::RefreshTokenRouterData> for FlexitiAccessTokenRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::RefreshTokenRouterData) -> Result<Self, Self::Error> {
        let auth_details = FlexitiAuthType::try_from(&item.connector_auth_type)?;

        Ok(Self {
            client_id: auth_details.client_id,
            client_secret: auth_details.client_secret,
            grant_type: FlexitiGranttype::ClientCredentials,
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, FlexitiAccessTokenResponse, T, AccessToken>>
    for RouterData<F, T, AccessToken>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, FlexitiAccessTokenResponse, T, AccessToken>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(AccessToken {
                token: item.response.access_token,
                expires: item.response.expires_in,
            }),
            ..item.data
        })
    }
}

// Auth Struct
pub struct FlexitiAuthType {
    pub(super) client_id: Secret<String>,
    pub(super) client_secret: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for FlexitiAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                client_id: api_key.to_owned(),
                client_secret: key1.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FlexitiAccessTokenResponse {
    access_token: Secret<String>,
    expires_in: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlexitiPaymentsResponse {
    redirection_url: url::Url,
    online_order_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlexitiSyncResponse {
    transaction_id: String,
    purchase: FlexitiPurchase,
}

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

// Since this is an alpha integration, we don't have access to all the status mapping. This needs to be updated.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FlexitiPurchaseStatus {
    Success,
    Failed,
}

// Since this is an alpha integration, we don't have access to all the status mapping. This needs to be updated.
impl From<FlexitiPurchaseStatus> for common_enums::AttemptStatus {
    fn from(item: FlexitiPurchaseStatus) -> Self {
        match item {
            FlexitiPurchaseStatus::Success => Self::Authorized,
            FlexitiPurchaseStatus::Failed => Self::Failure,
        }
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, FlexitiSyncResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, FlexitiSyncResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: common_enums::AttemptStatus::from(item.response.purchase.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    item.response.transaction_id.to_owned(),
                ),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, FlexitiPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, FlexitiPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: common_enums::AttemptStatus::AuthenticationPending,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    item.response.online_order_id.to_owned(),
                ),
                redirection_data: Box::new(Some(RedirectForm::from((
                    item.response.redirection_url,
                    Method::Get,
                )))),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
// REFUND :
// Type definition for RefundRequest
#[derive(Default, Debug, Serialize)]
pub struct FlexitiRefundRequest {
    pub amount: FloatMajorUnit,
}

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

// Type definition for Refund Response

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

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

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

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

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

#[derive(Debug, Serialize, Deserialize)]
pub struct FlexitiErrorResponse {
    pub message: String,
    pub error: String,
}


pub async fn call_create_connector_customer_if_required<F, Req, D>(
    state: &SessionState,
    customer: &Option<domain::Customer>,
    merchant_context: &domain::MerchantContext,
    merchant_connector_account: &helpers::MerchantConnectorAccountType,
    payment_data: &mut D,
    access_token: Option<&AccessToken>,
) -> RouterResult<Option<storage::CustomerUpdate>>
where
    F: Send + Clone + Sync,
    Req: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, Req, router_types::PaymentsResponseData>,
    RouterData<F, Req, router_types::PaymentsResponseData>: Feature<F, Req> + Send,

    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, Req, router_types::PaymentsResponseData>,
{
    let connector_name = payment_data.get_payment_attempt().connector.clone();

    match connector_name {
        Some(connector_name) => {
            let connector = api::ConnectorData::get_connector_by_name(
                &state.conf.connectors,
                &connector_name,
                api::GetToken::Connector,
                merchant_connector_account.get_mca_id(),
            )?;

            let label = {
                let connector_label = core_utils::get_connector_label(
                    payment_data.get_payment_intent().business_country,
                    payment_data.get_payment_intent().business_label.as_ref(),
                    payment_data
                        .get_payment_attempt()
                        .business_sub_label
                        .as_ref(),
                    &connector_name,
                );

                if let Some(connector_label) = merchant_connector_account
                    .get_mca_id()
                    .map(|mca_id| mca_id.get_string_repr().to_string())
                    .or(connector_label)
                {
                    connector_label
                } else {
                    let profile_id = payment_data
                        .get_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")?;

                    format!("{connector_name}_{}", profile_id.get_string_repr())
                }
            };

            let (should_call_connector, existing_connector_customer_id) =
                customers::should_call_connector_create_customer(
                    state, &connector, customer, &label,
                );

            if should_call_connector {
                // Create customer at connector and update the customer table to store this data
                let mut customer_router_data = payment_data
                    .construct_router_data(
                        state,
                        connector.connector.id(),
                        merchant_context,
                        customer,
                        merchant_connector_account,
                        None,
                        None,
                    )
                    .await?;

                customer_router_data.access_token = access_token.cloned();

                let connector_customer_id = customer_router_data
                    .create_connector_customer(state, &connector)
                    .await?;

                let customer_update = customers::update_connector_customer_in_customers(
                    &label,
                    customer.as_ref(),
                    connector_customer_id.clone(),
                )
                .await;

                payment_data.set_connector_customer_id(connector_customer_id);
                Ok(customer_update)
            } else {
                // Customer already created in previous calls use the same value, no need to update
                payment_data.set_connector_customer_id(
                    existing_connector_customer_id.map(ToOwned::to_owned),
                );
                Ok(None)
            }
        }
        None => Ok(None),
    }
}


pub struct ConnectorParams {
    /// base url
    pub base_url: String,
    /// secondary base url
    pub secondary_base_url: Option<String>,
}


pub struct AuthEventFilterValue {
    pub dimension: AuthEventDimensions,
    pub values: Vec<String>,
}


impl api::PaymentSync for Checkout {}


impl PaymentMethodsEnabled {
    /// Get payment_method
    #[cfg(feature = "v1")]
    pub fn get_payment_method(&self) -> Option<common_enums::PaymentMethod> {
        Some(self.payment_method)
    }
    /// Get payment_method_types
    #[cfg(feature = "v1")]
    pub fn get_payment_method_type(
        &self,
    ) -> Option<&Vec<payment_methods::RequestPaymentMethodTypes>> {
        self.payment_method_types.as_ref()
    }
}


pub struct Context {
    atomic_values: FxHashSet<EuclidValue>,
    numeric_values: FxHashMap<EuclidKey, EuclidValue>,
}


impl<T: serde::Serialize> ConvertRaw for T {
    type Output = T;
    fn convert_raw(self) -> Result<Self::Output, errors::KeyManagerClientError> {
        Ok(self)
    }
}


impl api::RefundSync for Mollie {}


OpenAPI Block Path: components.schemas.ApplePayPaymentTiming
{
  "type": "string",
  "enum": [
    "immediate",
    "recurring"
  ]
}


impl MerchantConnectorListResponse {
    pub fn to_merchant_connector_info(&self, connector_label: &String) -> MerchantConnectorInfo {
        MerchantConnectorInfo {
            connector_label: connector_label.to_string(),
            merchant_connector_id: self.id.clone(),
        }
    }
    pub fn get_connector_name(&self) -> common_enums::connector_enums::Connector {
        self.connector_name
    }
}


impl RefundSync for Riskified {}


pub struct CancelInputData {
    transaction_id: String,
}


pub struct ProphetpaySyncResponse {
    success: bool,
    pub response_text: String,
    #[serde(rename = "transactionID")]
    pub transaction_id: String,
}


pub fn check_currency(
    currency: enums::Currency,
) -> Result<enums::Currency, errors::ConnectorError> {
    if currency == enums::Currency::USD {
        Ok(currency)
    } else {
        Err(errors::ConnectorError::NotSupported {
            message: format!("currency {currency} is not supported for this merchant account"),
            connector: "Helcim",
        })?
    }
}


pub fn get_individual_surcharge_key_value_pairs(&self) -> Vec<(String, SurchargeDetails)> {
        self.surcharge_results
            .iter()
            .map(|(surcharge_key, surcharge_details)| {
                let key = Self::get_surcharge_details_redis_hashset_key(surcharge_key);
                (key, surcharge_details.to_owned())
            })
            .collect()
    }


File: crates/router/src/routes/recovery_webhooks.rs
 Public functions: 1

use actix_web::{web, HttpRequest, Responder};
use router_env::{instrument, tracing, Flow};

use super::app::AppState;
use crate::{
    core::{
        api_locking,
        webhooks::{self, types},
    },
    services::{api, authentication as auth},
    types::domain,
};

#[instrument(skip_all, fields(flow = ?Flow::IncomingWebhookReceive))]
pub async fn recovery_receive_incoming_webhook<W: types::OutgoingWebhookType>(
    state: web::Data<AppState>,
    req: HttpRequest,
    body: web::Bytes,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::ProfileId,
        common_utils::id_type::MerchantConnectorAccountId,
    )>,
) -> impl Responder {
    let flow = Flow::RecoveryIncomingWebhookReceive;
    let (merchant_id, profile_id, connector_id) = path.into_inner();

    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &req,
        (),
        |state, auth, _, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            webhooks::incoming_webhooks_wrapper::<W>(
                &flow,
                state.to_owned(),
                req_state,
                &req,
                merchant_context,
                auth.profile,
                &connector_id,
                body.clone(),
                false,
            )
        },
        &auth::MerchantIdAndProfileIdAuth {
            merchant_id,
            profile_id,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub async fn upload_file_to_user_theme_storage(
    state: SessionState,
    theme_id: String,
    user_from_token: UserFromToken,
    request: theme_api::UploadFileRequest,
) -> UserResponse<()> {
    let db_theme = state
        .store
        .find_theme_by_theme_id(theme_id)
        .await
        .to_not_found_response(UserErrors::ThemeNotFound)?;

    let user_role_info = user_from_token
        .get_role_info_from_db(&state)
        .await
        .attach_printable("Invalid role_id in JWT")?;
    let user_entity_type = user_role_info.get_entity_type();

    theme_utils::can_user_access_theme(&user_from_token, &user_entity_type, &db_theme).await?;
    theme_utils::upload_file_to_theme_bucket(
        &state,
        &theme_utils::get_specific_file_key(&db_theme.theme_id, &request.asset_name),
        request.asset_data.expose(),
    )
    .await?;

    Ok(ApplicationResponse::StatusOk)
}


pub struct KlarnaData {
    klarna: KlarnaDetails,
    #[serde(rename = "type")]
    payment_method_type: AirwallexPaymentType,
}


/// Get the merchant_details from MerchantAccount
    pub fn get_merchant_details(&self) -> &OptionalEncryptableValue {
        &self.merchant_details
    }


pub struct UserAuthenticationMethod {
    pub id: String,
    pub auth_id: String,
    pub owner_id: String,
    pub owner_type: enums::Owner,
    pub auth_type: enums::UserAuthType,
    pub private_config: Option<Encryption>,
    pub public_config: Option<serde_json::Value>,
    pub allow_signup: bool,
    pub created_at: PrimitiveDateTime,
    pub last_modified_at: PrimitiveDateTime,
    pub email_domain: String,
}


pub struct DisputeMetricsBucketIdentifier {
    pub dispute_stage: Option<DisputeStage>,
    pub connector: Option<String>,
    pub currency: Option<Currency>,
    #[serde(rename = "time_range")]
    pub time_bucket: TimeRange,
    #[serde(rename = "time_bucket")]
    #[serde(with = "common_utils::custom_serde::iso8601custom")]
    pub start_time: time::PrimitiveDateTime,
}


impl Responder {
        let flow = AnalyticsFlow::GetDisputeFilters;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let org_id = auth.merchant_account.get_org_id();
                let merchant_id = auth.merchant_account.get_id();
                let auth: AuthInfo = AuthInfo::MerchantLevel {
                    org_id: org_id.clone(),
                    merchant_ids: vec![merchant_id.clone()],
                };
                analytics::disputes::get_filters(&state.pool, req, &auth)
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::MerchantAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub async fn upsert_calculate_pcr_task(
    billing_connector_account: &domain::MerchantConnectorAccount,
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    recovery_intent_from_payment_intent: &domain_revenue_recovery::RecoveryPaymentIntent,
    business_profile: &domain::Profile,
    intent_retry_count: u16,
    payment_attempt_id: Option<id_type::GlobalAttemptId>,
    runner: storage::ProcessTrackerRunner,
    revenue_recovery_retry: diesel_enum::RevenueRecoveryAlgorithmType,
) -> CustomResult<webhooks::WebhookResponseTracker, errors::RevenueRecoveryError> {
    router_env::logger::info!("Starting calculate_job...");

    let task = "CALCULATE_WORKFLOW";

    let db = &*state.store;
    let payment_id = &recovery_intent_from_payment_intent.payment_id;

    // Create process tracker ID in the format: CALCULATE_WORKFLOW_{payment_intent_id}
    let process_tracker_id = format!("{runner}_{task}_{}", payment_id.get_string_repr());

    // Scheduled time is now because this will be the first entry in
    // process tracker and we dont want to wait
    let schedule_time = common_utils::date_time::now();

    let payment_attempt_id = payment_attempt_id
        .ok_or(error_stack::report!(
            errors::RevenueRecoveryError::PaymentAttemptIdNotFound
        ))
        .attach_printable("payment attempt id is required for calculate workflow tracking")?;

    // Check if a process tracker entry already exists for this payment intent
    let existing_entry = db
        .as_scheduler()
        .find_process_by_id(&process_tracker_id)
        .await
        .change_context(errors::RevenueRecoveryError::ProcessTrackerResponseError)
        .attach_printable(
            "Failed to check for existing calculate workflow process tracker entry",
        )?;

    match existing_entry {
        Some(existing_process) => {
            router_env::logger::error!(
                "Found existing CALCULATE_WORKFLOW task with  id: {}",
                existing_process.id
            );
        }
        None => {
            // No entry exists - create a new one
            router_env::logger::info!(
                "No existing CALCULATE_WORKFLOW task found for payment_intent_id: {}, creating new entry scheduled for 1 hour from now",
                payment_id.get_string_repr()
            );

            // Create tracking data
            let calculate_workflow_tracking_data = pcr::RevenueRecoveryWorkflowTrackingData {
                billing_mca_id: billing_connector_account.get_id(),
                global_payment_id: payment_id.clone(),
                merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
                profile_id: business_profile.get_id().to_owned(),
                payment_attempt_id,
                revenue_recovery_retry,
                invoice_scheduled_time: None,
            };

            let tag = ["PCR"];
            let task = "CALCULATE_WORKFLOW";
            let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;

            let process_tracker_entry = storage::ProcessTrackerNew::new(
                process_tracker_id,
                task,
                runner,
                tag,
                calculate_workflow_tracking_data,
                Some(1),
                schedule_time,
                common_types::consts::API_VERSION,
            )
            .change_context(errors::RevenueRecoveryError::ProcessTrackerCreationError)
            .attach_printable("Failed to construct calculate workflow process tracker entry")?;

            // Insert into process tracker with status New
            db.as_scheduler()
                .insert_process(process_tracker_entry)
                .await
                .change_context(errors::RevenueRecoveryError::ProcessTrackerResponseError)
                .attach_printable(
                    "Failed to enter calculate workflow process_tracker_entry in DB",
                )?;

            router_env::logger::info!(
                "Successfully created new CALCULATE_WORKFLOW task for payment_intent_id: {}",
                payment_id.get_string_repr()
            );

            metrics::TASKS_ADDED_COUNT.add(
                1,
                router_env::metric_attributes!(("flow", "CalculateWorkflow")),
            );
        }
    }

    Ok(webhooks::WebhookResponseTracker::Payment {
        payment_id: payment_id.clone(),
        status: recovery_intent_from_payment_intent.status,
    })
}


impl Responder {
    let payload = json_payload.into_inner();
    let flow = Flow::PmAuthExchangeToken;
    let api_auth = auth::ApiKeyAuth::default();

    let (auth, _) = match crate::services::authentication::check_client_secret_and_get_auth(
        req.headers(),
        &payload,
        api_auth,
    ) {
        Ok((auth, _auth_flow)) => (auth, _auth_flow),
        Err(e) => return api::log_and_return_error_response(e),
    };
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth, payload, _| {
            let merchant_context = crate::types::domain::MerchantContext::NormalMerchant(Box::new(
                crate::types::domain::Context(auth.merchant_account, auth.key_store),
            ));
            crate::core::pm_auth::exchange_token_core(state, merchant_context, payload)
        },
        &*auth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


File: crates/diesel_models/src/query/gsm.rs
 Public functions: 5

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

use crate::{
    errors, gsm::*, query::generics, schema::gateway_status_map::dsl, PgPooledConn, StorageResult,
};

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

impl GatewayStatusMap {
    pub async fn find(
        conn: &PgPooledConn,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::connector
                .eq(connector)
                .and(dsl::flow.eq(flow))
                .and(dsl::sub_flow.eq(sub_flow))
                .and(dsl::code.eq(code))
                .and(dsl::message.eq(message)),
        )
        .await
    }

    pub async fn retrieve_decision(
        conn: &PgPooledConn,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> StorageResult<String> {
        Self::find(conn, connector, flow, sub_flow, code, message)
            .await
            .map(|item| item.decision)
    }

    pub async fn update(
        conn: &PgPooledConn,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
        gsm: GatewayStatusMappingUpdate,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<
            <Self as HasTable>::Table,
            GatewayStatusMapperUpdateInternal,
            _,
            _,
        >(
            conn,
            dsl::connector
                .eq(connector)
                .and(dsl::flow.eq(flow))
                .and(dsl::sub_flow.eq(sub_flow))
                .and(dsl::code.eq(code))
                .and(dsl::message.eq(message)),
            gsm.into(),
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(errors::DatabaseError::NotFound)
                .attach_printable("Error while updating gsm entry")
        })
    }

    pub async fn delete(
        conn: &PgPooledConn,
        connector: String,
        flow: String,
        sub_flow: String,
        code: String,
        message: String,
    ) -> StorageResult<bool> {
        generics::generic_delete::<<Self as HasTable>::Table, _>(
            conn,
            dsl::connector
                .eq(connector)
                .and(dsl::flow.eq(flow))
                .and(dsl::sub_flow.eq(sub_flow))
                .and(dsl::code.eq(code))
                .and(dsl::message.eq(message)),
        )
        .await
    }
}


pub struct TrustlyDetails {
    shopper_name: Secret<String>,
    country_code: enums::CountryAlpha2,
}


impl ApiEventMetricsBucketIdentifier {
    pub fn new(normalized_time_range: TimeRange) -> Self {
        Self {
            time_bucket: normalized_time_range,
            start_time: normalized_time_range.start_time,
        }
    }
}


impl StripeApis {
    pub fn server(state: routes::AppState) -> Scope {
        let max_depth = 10;
        let strict = false;
        web::scope("/vs/v1")
            .app_data(web::Data::new(serde_qs::Config::new(max_depth, strict)))
            .service(app::SetupIntents::server(state.clone()))
            .service(app::PaymentIntents::server(state.clone()))
            .service(app::Refunds::server(state.clone()))
            .service(app::Customers::server(state.clone()))
            .service(app::Webhooks::server(state.clone()))
            .service(app::Mandates::server(state))
    }
}


pub struct SilverflowRefundRequest {
    #[serde(rename = "refundAmount")]
    pub refund_amount: MinorUnit,
    pub reference: String,
}


pub fn parse_facilitapay_error_response(
    res: Response,
    event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
    let status_code = res.status_code;
    let response_body_bytes = res.response.clone();

    let (message, raw_error) =
        match response_body_bytes.parse_struct::<serde_json::Value>("FacilitapayErrorResponse") {
            Ok(json_value) => {
                event_builder.map(|i| i.set_response_body(&json_value));

                let message = extract_message_from_json(&json_value);
                (
                    message,
                    serde_json::to_string(&json_value).unwrap_or_default(),
                )
            }
            Err(_) => match String::from_utf8(response_body_bytes.to_vec()) {
                Ok(text) => {
                    event_builder.map(|i| i.set_response_body(&text));
                    (text.clone(), text)
                }
                Err(_) => (
                    "Invalid response format received".to_string(),
                    format!(
                    "Unable to parse response as JSON or UTF-8 string. Status code: {status_code}",
                ),
                ),
            },
        };

    Ok(ErrorResponse {
        status_code,
        code: consts::NO_ERROR_CODE.to_string(),
        message,
        reason: Some(raw_error),
        attempt_status: None,
        connector_transaction_id: None,
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    })
}


impl Responder {
    let flow = Flow::DecisionManagerRetrieveConfig;
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        (),
        |state, auth: auth::AuthenticationData, _, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            surcharge_decision_config::retrieve_surcharge_decision_config(state, merchant_context)
        },
        #[cfg(not(feature = "release"))]
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::MerchantSurchargeDecisionManagerRead,
            },
            req.headers(),
        ),
        #[cfg(feature = "release")]
        &auth::JWTAuth {
            permission: Permission::MerchantSurchargeDecisionManagerRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


OpenAPI Block Path: components.schemas.MerchantAccountData
{
  "oneOf": [
    {
      "type": "object",
      "required": [
        "iban"
      ],
      "properties": {
        "iban": {
          "type": "object",
          "description": "IBAN-based account for international transfers",
          "required": [
            "iban",
            "name"
          ],
          "properties": {
            "iban": {
              "type": "string",
              "description": "International Bank Account Number (up to 34 characters)"
            },
            "name": {
              "type": "string",
              "description": "Account holder name"
            },
            "connector_recipient_id": {
              "type": "string",
              "nullable": true
            }
          }
        }
      }
    },
    {
      "type": "object",
      "required": [
        "bacs"
      ],
      "properties": {
        "bacs": {
          "type": "object",
          "description": "UK BACS payment system",
          "required": [
            "account_number",
            "sort_code",
            "name"
          ],
          "properties": {
            "account_number": {
              "type": "string",
              "description": "8-digit UK account number"
            },
            "sort_code": {
              "type": "string",
              "description": "6-digit UK sort code",
              "example": "123456"
            },
            "name": {
              "type": "string",
              "description": "Account holder name"
            },
            "connector_recipient_id": {
              "type": "string",
              "nullable": true
            }
          }
        }
      }
    },
    {
      "type": "object",
      "required": [
        "faster_payments"
      ],
      "properties": {
        "faster_payments": {
          "type": "object",
          "description": "UK Faster Payments (instant transfers)",
          "required": [
            "account_number",
            "sort_code",
            "name"
          ],
          "properties": {
            "account_number": {
              "type": "string",
              "description": "8-digit UK account number"
            },
            "sort_code": {
              "type": "string",
              "description": "6-digit UK sort code"
            },
            "name": {
              "type": "string",
              "description": "Account holder name"
            },
            "connector_recipient_id": {
              "type": "string",
              "nullable": true
            }
          }
        }
      }
    },
    {
      "type": "object",
      "required": [
        "sepa"
      ],
      "properties": {
        "sepa": {
          "type": "object",
          "description": "SEPA payments (Euro zone)",
          "required": [
            "iban",
            "name"
          ],
          "properties": {
            "iban": {
              "type": "string",
              "description": "IBAN for SEPA transfers",
              "example": "FR1420041010050500013M02606"
            },
            "name": {
              "type": "string",
              "description": "Account holder name"
            },
            "connector_recipient_id": {
              "type": "string",
              "nullable": true
            }
          }
        }
      }
    },
    {
      "type": "object",
      "required": [
        "sepa_instant"
      ],
      "properties": {
        "sepa_instant": {
          "type": "object",
          "description": "SEPA Instant payments (10-second transfers)",
          "required": [
            "iban",
            "name"
          ],
          "properties": {
            "iban": {
              "type": "string",
              "description": "IBAN for instant SEPA transfers",
              "example": "DE89370400440532013000"
            },
            "name": {
              "type": "string",
              "description": "Account holder name"
            },
            "connector_recipient_id": {
              "type": "string",
              "nullable": true
            }
          }
        }
      }
    },
    {
      "type": "object",
      "required": [
        "elixir"
      ],
      "properties": {
        "elixir": {
          "type": "object",
          "description": "Polish Elixir payment system",
          "required": [
            "account_number",
            "iban",
            "name"
          ],
          "properties": {
            "account_number": {
              "type": "string",
              "description": "Polish account number (26 digits)",
              "example": "12345678901234567890123456"
            },
            "iban": {
              "type": "string",
              "description": "Polish IBAN (28 chars)",
              "example": "PL27114020040000300201355387"
            },
            "name": {
              "type": "string",
              "description": "Account holder name"
            },
            "connector_recipient_id": {
              "type": "string",
              "nullable": true
            }
          }
        }
      }
    },
    {
      "type": "object",
      "required": [
        "bankgiro"
      ],
      "properties": {
        "bankgiro": {
          "type": "object",
          "description": "Swedish Bankgiro system",
          "required": [
            "number",
            "name"
          ],
          "properties": {
            "number": {
              "type": "string",
              "description": "Bankgiro number (7-8 digits)",
              "example": "5402-9656"
            },
            "name": {
              "type": "string",
              "description": "Account holder name",
              "example": "Erik Andersson"
            },
            "connector_recipient_id": {
              "type": "string",
              "nullable": true
            }
          }
        }
      }
    },
    {
      "type": "object",
      "required": [
        "plusgiro"
      ],
      "properties": {
        "plusgiro": {
          "type": "object",
          "description": "Swedish Plusgiro system",
          "required": [
            "number",
            "name"
          ],
          "properties": {
            "number": {
              "type": "string",
              "description": "Plusgiro number (2-8 digits)",
              "example": "4789-2"
            },
            "name": {
              "type": "string",
              "description": "Account holder name",
              "example": "Anna Larsson"
            },
            "connector_recipient_id": {
              "type": "string",
              "nullable": true
            }
          }
        }
      }
    }
  ]
}


impl api::PaymentAuthorize for Rapyd {}

impl api::PaymentAuthorize for for Rapyd


pub struct JpmorganCard {
    account_number: Secret<String>,
    expiry: Expiry,
}


Documentation: api-reference/v1/routing/routing--retrieve-default-for-profile.mdx
# Type: Doc File

---
openapi: get /routing/default/profile
---


pub async fn schedule_next_dispute_list_task(
    db: &dyn StorageInterface,
    tracking_data: &api::DisputeListPTData,
    dispute_polling_interval: i32,
) -> Result<(), errors::ProcessTrackerError> {
    let new_created_till = tracking_data
        .created_till
        .checked_add(time::Duration::hours(i64::from(dispute_polling_interval)))
        .ok_or(sch_errors::ProcessTrackerError::TypeConversionError)?;

    let fetch_request = hyperswitch_domain_models::router_request_types::FetchDisputesRequestData {
        created_from: tracking_data.created_till,
        created_till: new_created_till,
    };

    disputes::add_dispute_list_task_to_pt(
        db,
        &tracking_data.connector_name,
        tracking_data.merchant_id.clone(),
        tracking_data.merchant_connector_id.clone(),
        tracking_data.profile_id.clone(),
        fetch_request,
    )
    .await?;
    Ok(())
}


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

use masking::Secret;
use router::types::{self, storage::enums};
use test_utils::connector_auth;

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

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

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

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

static CONNECTOR: EbanxTest = EbanxTest {};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Connector dependent test cases goes here

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


impl ApiEventMetric for GenerateFingerprintRequest {}

impl ApiEventMetric for for GenerateFingerprintRequest


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

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

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

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

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

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

        match response.error {
            Some(error) => {
                let details = error.details.map(|details| {
                    details
                        .iter()
                        .map(|detail| {
                            format!(
                                "{}: {}",
                                detail
                                    .field
                                    .clone()
                                    .unwrap_or("No Field Provided".to_string()),
                                detail
                                    .message
                                    .clone()
                                    .unwrap_or("No Message Provided".to_string())
                            )
                        })
                        .collect::<Vec<String>>()
                        .join(", ")
                });
                Ok(ErrorResponse {
                    status_code: res.status_code,
                    code: error.code.unwrap_or(consts::NO_ERROR_CODE.to_string()),
                    message: response
                        .response_type
                        .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
                    reason: match details {
                        Some(details) => Some(format!(
                            "{} {}",
                            error.message.unwrap_or("".to_string()),
                            details
                        )),
                        None => error.message,
                    },
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                })
            }
            None => Ok(ErrorResponse {
                status_code: res.status_code,
                code: consts::NO_ERROR_CODE.to_string(),
                message: response
                    .response_type
                    .clone()
                    .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
                reason: response.response_type,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
        }
    }
}


pub struct GetCardResponse {
    pub card: AddCardResponse,
}


pub struct PayoutQuoteResponse {
    pub source_id: Secret<String>,
    pub destination_currency_code: Currency,
    pub amount: FloatMajorUnit,
    pub source_currency_code: Currency,
    pub include_fee: bool,
    pub fees: Vec<FeesType>,
    pub payment_reference: String,
}


pub struct TokenizedCard {
    number: cards::CardNumber,
    expiration_month: Secret<String>,
    expiration_year: Secret<String>,
    cryptogram: Option<Secret<String>>,
    transaction_type: TransactionType,
}


pub struct NexixpayPaymentsRequest {
    order: Order,
    #[serde(flatten)]
    payment_data: NexixpayPaymentsRequestData,
}


impl IncomingWebhook for Wise {
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn common_utils::crypto::VerifySignature + Send>, ConnectorError> {
        Ok(Box::new(common_utils::crypto::RsaSha256))
    }

    fn get_webhook_source_verification_signature(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            request
                .headers
                .get("X-Signature-SHA256")
                .map(|header_value| header_value.as_bytes().to_vec())
                .ok_or(ConnectorError::WebhookSignatureNotFound.into())
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }

    fn get_webhook_source_verification_message(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            Ok(request.body.to_vec())
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }

    fn get_webhook_object_reference_id(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            let payload: wise::WisePayoutsWebhookBody = request
                .body
                .parse_struct("WisePayoutsWebhookBody")
                .change_context(ConnectorError::WebhookReferenceIdNotFound)?;

            Ok(api_models::webhooks::ObjectReferenceId::PayoutId(
                api_models::webhooks::PayoutIdType::ConnectorPayoutId(
                    payload.data.resource.id.to_string(),
                ),
            ))
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }

    fn get_webhook_event_type(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            let payload: wise::WisePayoutsWebhookBody = request
                .body
                .parse_struct("WisePayoutsWebhookBody")
                .change_context(ConnectorError::WebhookReferenceIdNotFound)?;

            Ok(transformers::get_wise_webhooks_event(
                payload.data.current_state,
            ))
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }

    fn get_webhook_resource_object(
        &self,
        #[cfg(feature = "payouts")] request: &IncomingWebhookRequestDetails<'_>,
        #[cfg(not(feature = "payouts"))] _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, ConnectorError> {
        #[cfg(feature = "payouts")]
        {
            let payload: wise::WisePayoutsWebhookBody = request
                .body
                .parse_struct("WisePayoutsWebhookBody")
                .change_context(ConnectorError::WebhookReferenceIdNotFound)?;

            Ok(Box::new(wise::WisePayoutSyncResponse::from(payload.data)))
        }
        #[cfg(not(feature = "payouts"))]
        {
            Err(report!(ConnectorError::WebhooksNotImplemented))
        }
    }
}


pub async fn get_cache_store(
    config: &Settings,
    shut_down_signal: oneshot::Sender<()


impl PaymentMetricAccumulator for AverageAccumulator {
    type MetricOutput = Option<f64>;

    fn add_metrics_bucket(&mut self, metrics: &PaymentMetricRow) {
        let total = metrics.total.as_ref().and_then(ToPrimitive::to_u32);
        let count = metrics.count.and_then(|total| u32::try_from(total).ok());

        match (total, count) {
            (Some(total), Some(count)) => {
                self.total += total;
                self.count += count;
            }
            _ => {
                logger::error!(message="Dropping metrics for average accumulator", metric=?metrics);
            }
        }
    }

    fn collect(self) -> Self::MetricOutput {
        if self.count == 0 {
            None
        } else {
            Some(f64::from(self.total) / f64::from(self.count))
        }
    }
}

impl PaymentMetricAccumulator for for AverageAccumulator


pub fn set_validate_result(self) -> NetworkTokenizationBuilder<'a, CardRequestValidated> {
        NetworkTokenizationBuilder {
            state: std::marker::PhantomData,
            customer: self.customer,
            card: self.card,
            card_cvc: self.card_cvc,
            network_token: self.network_token,
            stored_card: self.stored_card,
            stored_token: self.stored_token,
            payment_method_response: self.payment_method_response,
            card_tokenized: self.card_tokenized,
            error_code: self.error_code,
            error_message: self.error_message,
        }
    }


pub struct FluidData {
    value: Secret<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    descriptor: Option<String>,
}


pub async fn create_profile_acquirer(
    state: SessionState,
    request: profile_acquirer::ProfileAcquirerCreate,
    merchant_context: domain::MerchantContext,
) -> RouterResponse<profile_acquirer::ProfileAcquirerResponse> {
    let db = state.store.as_ref();
    let profile_acquirer_id = common_utils::generate_profile_acquirer_id_of_default_length();
    let key_manager_state: KeyManagerState = (&state).into();
    let merchant_key_store = merchant_context.get_merchant_key_store();

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

    let incoming_acquirer_config = common_types::domain::AcquirerConfig {
        acquirer_assigned_merchant_id: request.acquirer_assigned_merchant_id.clone(),
        merchant_name: request.merchant_name.clone(),
        network: request.network.clone(),
        acquirer_bin: request.acquirer_bin.clone(),
        acquirer_ica: request.acquirer_ica.clone(),
        acquirer_fraud_rate: request.acquirer_fraud_rate,
    };

    // Check for duplicates before proceeding

    business_profile
        .acquirer_config_map
        .as_ref()
        .map_or(Ok(()), |configs_wrapper| {
            match configs_wrapper.0.values().any(|existing_config| existing_config == &incoming_acquirer_config) {
                true => Err(error_stack::report!(
                    errors::ApiErrorResponse::GenericDuplicateError {
                        message: format!(
                            "Duplicate acquirer configuration found for profile_id: {}. Conflicting configuration: {:?}",
                            request.profile_id.get_string_repr(),
                            incoming_acquirer_config
                        ),
                    }
                )),
                false => Ok(()),
            }
        })?;

    // Get a mutable reference to the HashMap inside AcquirerConfigMap,
    // initializing if it's None or the inner HashMap is not present.
    let configs_map = &mut business_profile
        .acquirer_config_map
        .get_or_insert_with(|| {
            common_types::domain::AcquirerConfigMap(std::collections::HashMap::new())
        })
        .0;

    configs_map.insert(
        profile_acquirer_id.clone(),
        incoming_acquirer_config.clone(),
    );

    let profile_update = domain::ProfileUpdate::AcquirerConfigMapUpdate {
        acquirer_config_map: business_profile.acquirer_config_map.clone(),
    };
    let updated_business_profile = db
        .update_profile_by_profile_id(
            &key_manager_state,
            merchant_key_store,
            business_profile,
            profile_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update business profile with new acquirer config")?;

    let updated_acquire_details = updated_business_profile
        .acquirer_config_map
        .as_ref()
        .and_then(|acquirer_configs_wrapper| acquirer_configs_wrapper.0.get(&profile_acquirer_id))
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get updated acquirer config")?;

    let response = profile_acquirer::ProfileAcquirerResponse::from((
        profile_acquirer_id,
        &request.profile_id,
        updated_acquire_details,
    ));

    Ok(api::ApplicationResponse::Json(response))
}


File: crates/analytics/src/api_event/metrics/api_count.rs

use std::collections::HashSet;

use api_models::analytics::{
    api_event::{ApiEventDimensions, ApiEventFilters, ApiEventMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::ApiEventMetricRow;
use crate::{
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};

#[derive(Default)]
pub(super) struct ApiCount;

#[async_trait::async_trait]
impl<T> super::ApiEventMetric<T> for ApiCount
where
    T: AnalyticsDataSource + super::ApiEventMetricAnalytics,
    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: &[ApiEventDimensions],
        merchant_id: &common_utils::id_type::MerchantId,
        filters: &ApiEventFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(ApiEventMetricsBucketIdentifier, ApiEventMetricRow)>> {
        let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::ApiEvents);

        query_builder
            .add_select_column(Aggregate::Count {
                field: None,
                alias: Some("api_count"),
            })
            .switch()?;
        if !filters.flow_type.is_empty() {
            query_builder
                .add_filter_in_range_clause(ApiEventDimensions::FlowType, &filters.flow_type)
                .attach_printable("Error adding flow_type filter")
                .switch()?;
        }
        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;
        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .add_filter_clause("merchant_id", merchant_id)
            .switch()?;

        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;

        query_builder
            .execute_query::<ApiEventMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    ApiEventMetricsBucketIdentifier::new(TimeRange {
                        start_time: match (granularity, i.start_bucket) {
                            (Some(g), Some(st)) => g.clip_to_start(st)?,
                            _ => time_range.start_time,
                        },
                        end_time: granularity.as_ref().map_or_else(
                            || Ok(time_range.end_time),
                            |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                        )?,
                    }),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(ApiEventMetricsBucketIdentifier, ApiEventMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


impl api::PaymentVoid for Shift4 {}


File: crates/router/src/types/storage/business_profile.rs

pub use diesel_models::business_profile::{Profile, ProfileNew, ProfileUpdateInternal};


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


impl api::Refund for Trustpayments {}


pub(crate) fn api_error_derive_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    let variants = match &ast.data {
        Data::Enum(e) => &e.variants,
        _ => return Err(non_enum_error()),
    };

    let type_properties = ast.get_type_properties()?;

    let mut variants_properties_map = HashMap::new();
    for variant in variants {
        let variant_properties = variant.get_variant_properties()?;
        check_missing_attributes(variant, &variant_properties)?;

        variants_properties_map.insert(variant, variant_properties);
    }

    let error_type_fn = implement_error_type(name, &type_properties, &variants_properties_map);
    let error_code_fn = implement_error_code(name, &variants_properties_map);
    let error_message_fn = implement_error_message(name, &variants_properties_map);
    let serialize_impl = implement_serialize(
        name,
        (&impl_generics, &ty_generics, where_clause),
        &type_properties,
        &variants_properties_map,
    );

    Ok(quote! {
        #[automatically_derived]
        impl #impl_generics std::error::Error for #name #ty_generics #where_clause {}

        #[automatically_derived]
        impl #impl_generics #name #ty_generics #where_clause {
            #error_type_fn
            #error_code_fn
            #error_message_fn
        }

        #serialize_impl
    })
}


pub struct Aci {
    amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}


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


impl api::PaymentVoid for HyperswitchVault {}

impl api::PaymentVoid for for HyperswitchVault


pub struct NetworkResponse {
    address_verification_result: Option<Secret<String>>,
    address_verification_result_code: Option<Secret<String>>,
    card_verification_result_code: Option<Secret<String>>,
}


pub struct ProphetpayVoidResponse {
    pub success: bool,
    pub response_text: String,
    #[serde(rename = "transactionID")]
    pub transaction_id: String,
}


pub struct ApplePayMethodData {
    apple_pay_payment_token: Secret<String>,
}


pub struct SilverflowRouterData<T> {
    pub amount: MinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}


pub struct CoingateWebhookBody {
    pub token: Secret<String>,
    pub status: CoingatePaymentStatus,
    pub id: i64,
}


impl ConnectorSpecifications for Nexixpay {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&*NEXIXPAY_CONNECTOR_INFO)
    }

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

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

impl ConnectorSpecifications for for Nexixpay


impl PaymentSession for Riskified {}