File: crates/scheduler/src/utils.rs
 Public functions: 15

use std::sync;

use common_utils::errors::CustomResult;
use diesel_models::enums::{self, ProcessTrackerStatus};
pub use diesel_models::process_tracker as storage;
use error_stack::{report, ResultExt};
use redis_interface::{RedisConnectionPool, RedisEntryId};
use router_env::{instrument, tracing};
use uuid::Uuid;

use super::{
    consumer::{self, types::process_data, workflows},
    env::logger,
};
use crate::{
    configs::settings::SchedulerSettings, consumer::types::ProcessTrackerBatch, errors,
    flow::SchedulerFlow, metrics, SchedulerInterface, SchedulerSessionState,
};

pub async fn divide_and_append_tasks<T>(
    state: &T,
    flow: SchedulerFlow,
    tasks: Vec<storage::ProcessTracker>,
    settings: &SchedulerSettings,
) -> CustomResult<(), errors::ProcessTrackerError>
where
    T: SchedulerInterface + Send + Sync + ?Sized,
{
    let batches = divide(tasks, settings);
    // Safety: Assuming we won't deal with more than `u64::MAX` batches at once
    #[allow(clippy::as_conversions)]
    metrics::BATCHES_CREATED.add(batches.len() as u64, &[]); // Metrics
    for batch in batches {
        let result = update_status_and_append(state, flow, batch).await;
        match result {
            Ok(_) => (),
            Err(error) => logger::error!(?error),
        }
    }
    Ok(())
}

pub async fn update_status_and_append<T>(
    state: &T,
    flow: SchedulerFlow,
    pt_batch: ProcessTrackerBatch,
) -> CustomResult<(), errors::ProcessTrackerError>
where
    T: SchedulerInterface + Send + Sync + ?Sized,
{
    let process_ids: Vec<String> = pt_batch
        .trackers
        .iter()
        .map(|process| process.id.to_owned())
        .collect();
    match flow {
        SchedulerFlow::Producer => state
            .process_tracker_update_process_status_by_ids(
                process_ids,
                storage::ProcessTrackerUpdate::StatusUpdate {
                    status: ProcessTrackerStatus::Processing,
                    business_status: None,
                },
            )
            .await
            .map_or_else(
                |error| {
                    logger::error!(?error, "Error while updating process status");
                    Err(error.change_context(errors::ProcessTrackerError::ProcessUpdateFailed))
                },
                |count| {
                    logger::debug!("Updated status of {count} processes");
                    Ok(())
                },
            ),
        SchedulerFlow::Cleaner => {
            let res = state
                .reinitialize_limbo_processes(process_ids, common_utils::date_time::now())
                .await;
            match res {
                Ok(count) => {
                    logger::debug!("Reinitialized {count} processes");
                    Ok(())
                }
                Err(error) => {
                    logger::error!(?error, "Error while reinitializing processes");
                    Err(error.change_context(errors::ProcessTrackerError::ProcessUpdateFailed))
                }
            }
        }
        _ => {
            let error = format!("Unexpected scheduler flow {flow:?}");
            logger::error!(%error);
            Err(report!(errors::ProcessTrackerError::UnexpectedFlow).attach_printable(error))
        }
    }?;

    let field_value_pairs = pt_batch.to_redis_field_value_pairs()?;

    match state
        .stream_append_entry(
            &pt_batch.stream_name,
            &RedisEntryId::AutoGeneratedID,
            field_value_pairs,
        )
        .await
        .change_context(errors::ProcessTrackerError::BatchInsertionFailed)
    {
        Ok(x) => Ok(x),
        Err(mut err) => {
            let update_res = state
                .process_tracker_update_process_status_by_ids(
                    pt_batch
                        .trackers
                        .iter()
                        .map(|process| process.id.clone())
                        .collect(),
                    storage::ProcessTrackerUpdate::StatusUpdate {
                        status: ProcessTrackerStatus::Processing,
                        business_status: None,
                    },
                )
                .await
                .map_or_else(
                    |error| {
                        logger::error!(?error, "Error while updating process status");
                        Err(error.change_context(errors::ProcessTrackerError::ProcessUpdateFailed))
                    },
                    |count| {
                        logger::debug!("Updated status of {count} processes");
                        Ok(())
                    },
                );

            match update_res {
                Ok(_) => (),
                Err(inner_err) => {
                    err.extend_one(inner_err);
                }
            };

            Err(err)
        }
    }
}

pub fn divide(
    tasks: Vec<storage::ProcessTracker>,
    conf: &SchedulerSettings,
) -> Vec<ProcessTrackerBatch> {
    let now = common_utils::date_time::now();
    let batch_size = conf.producer.batch_size;
    divide_into_batches(batch_size, tasks, now, conf)
}

pub fn divide_into_batches(
    batch_size: usize,
    tasks: Vec<storage::ProcessTracker>,
    batch_creation_time: time::PrimitiveDateTime,
    conf: &SchedulerSettings,
) -> Vec<ProcessTrackerBatch> {
    let batch_id = Uuid::new_v4().to_string();

    tasks
        .chunks(batch_size)
        .fold(Vec::new(), |mut batches, item| {
            let batch = ProcessTrackerBatch {
                id: batch_id.clone(),
                group_name: conf.consumer.consumer_group.clone(),
                stream_name: conf.stream.clone(),
                connection_name: String::new(),
                created_time: batch_creation_time,
                rule: String::new(), // is it required?
                trackers: item.to_vec(),
            };
            batches.push(batch);

            batches
        })
}

pub async fn get_batches(
    conn: &RedisConnectionPool,
    stream_name: &str,
    group_name: &str,
    consumer_name: &str,
) -> CustomResult<Vec<ProcessTrackerBatch>, errors::ProcessTrackerError> {
    let response = match conn
        .stream_read_with_options(
            stream_name,
            RedisEntryId::UndeliveredEntryID,
            // Update logic for collecting to Vec and flattening, if count > 1 is provided
            Some(1),
            None,
            Some((group_name, consumer_name)),
        )
        .await
    {
        Ok(response) => response,
        Err(error) => {
            if let redis_interface::errors::RedisError::StreamEmptyOrNotAvailable =
                error.current_context()
            {
                logger::debug!("No batches processed as stream is empty");
                return Ok(Vec::new());
            } else {
                return Err(error.change_context(errors::ProcessTrackerError::BatchNotFound));
            }
        }
    };

    metrics::BATCHES_CONSUMED.add(1, &[]);

    let (batches, entry_ids): (Vec<Vec<ProcessTrackerBatch>>, Vec<Vec<String>>) = response.into_values().map(|entries| {
        entries.into_iter().try_fold(
            (Vec::new(), Vec::new()),
            |(mut batches, mut entry_ids), entry| {
                // Redis entry ID
                entry_ids.push(entry.0);
                // Value HashMap
                batches.push(ProcessTrackerBatch::from_redis_stream_entry(entry.1)?);

                Ok((batches, entry_ids))
            },
        )
    }).collect::<CustomResult<Vec<(Vec<ProcessTrackerBatch>, Vec<String>)>, errors::ProcessTrackerError>>()?
    .into_iter()
    .unzip();
    // Flattening the Vec's since the count provided above is 1. This needs to be updated if a
    // count greater than 1 is provided.
    let batches = batches.into_iter().flatten().collect::<Vec<_>>();
    let entry_ids = entry_ids.into_iter().flatten().collect::<Vec<_>>();

    conn.stream_acknowledge_entries(&stream_name.into(), group_name, entry_ids.clone())
        .await
        .map_err(|error| {
            logger::error!(?error, "Error acknowledging batch in stream");
            error.change_context(errors::ProcessTrackerError::BatchUpdateFailed)
        })?;
    conn.stream_delete_entries(&stream_name.into(), entry_ids.clone())
        .await
        .map_err(|error| {
            logger::error!(?error, "Error deleting batch from stream");
            error.change_context(errors::ProcessTrackerError::BatchDeleteFailed)
        })?;

    Ok(batches)
}

pub fn get_process_tracker_id<'a>(
    runner: storage::ProcessTrackerRunner,
    task_name: &'a str,
    txn_id: &'a str,
    merchant_id: &'a common_utils::id_type::MerchantId,
) -> String {
    format!(
        "{runner}_{task_name}_{txn_id}_{}",
        merchant_id.get_string_repr()
    )
}

pub fn get_process_tracker_id_for_dispute_list<'a>(
    runner: storage::ProcessTrackerRunner,
    merchant_connector_account_id: &'a common_utils::id_type::MerchantConnectorAccountId,
    created_from: time::PrimitiveDateTime,
    merchant_id: &'a common_utils::id_type::MerchantId,
) -> String {
    format!(
        "{runner}_{:04}{}{:02}{:02}_{}_{}",
        created_from.year(),
        created_from.month(),
        created_from.day(),
        created_from.hour(),
        merchant_connector_account_id.get_string_repr(),
        merchant_id.get_string_repr()
    )
}

pub fn get_time_from_delta(delta: Option<i32>) -> Option<time::PrimitiveDateTime> {
    delta.map(|t| common_utils::date_time::now().saturating_add(time::Duration::seconds(t.into())))
}

#[instrument(skip_all)]
pub async fn consumer_operation_handler<E, T>(
    state: T,
    settings: sync::Arc<SchedulerSettings>,
    error_handler_fun: E,
    workflow_selector: impl workflows::ProcessTrackerWorkflows<T> + 'static + Copy + std::fmt::Debug,
) where
    // Error handler function
    E: FnOnce(error_stack::Report<errors::ProcessTrackerError>),
    T: SchedulerSessionState + Send + Sync + 'static,
{
    match consumer::consumer_operations(&state, &settings, workflow_selector).await {
        Ok(_) => (),
        Err(err) => error_handler_fun(err),
    }
}

pub fn add_histogram_metrics(
    pickup_time: &time::PrimitiveDateTime,
    task: &mut storage::ProcessTracker,
    stream_name: &str,
) {
    #[warn(clippy::option_map_unit_fn)]
    if let Some((schedule_time, runner)) = task.schedule_time.as_ref().zip(task.runner.as_ref()) {
        let pickup_schedule_delta = (*pickup_time - *schedule_time).as_seconds_f64();
        logger::info!("Time delta for scheduled tasks: {pickup_schedule_delta} seconds");
        let runner_name = runner.clone();
        metrics::CONSUMER_OPS.record(
            pickup_schedule_delta,
            router_env::metric_attributes!((stream_name.to_owned(), runner_name)),
        );
    };
}

pub fn get_schedule_time(
    mapping: process_data::ConnectorPTMapping,
    merchant_id: &common_utils::id_type::MerchantId,
    retry_count: i32,
) -> Option<i32> {
    let mapping = match mapping.custom_merchant_mapping.get(merchant_id) {
        Some(map) => map.clone(),
        None => mapping.default_mapping,
    };

    // For first try, get the `start_after` time
    if retry_count == 0 {
        Some(mapping.start_after)
    } else {
        get_delay(retry_count, &mapping.frequencies)
    }
}

pub fn get_pm_schedule_time(
    mapping: process_data::PaymentMethodsPTMapping,
    pm: enums::PaymentMethod,
    retry_count: i32,
) -> Option<i32> {
    let mapping = match mapping.custom_pm_mapping.get(&pm) {
        Some(map) => map.clone(),
        None => mapping.default_mapping,
    };

    if retry_count == 0 {
        Some(mapping.start_after)
    } else {
        get_delay(retry_count, &mapping.frequencies)
    }
}

pub fn get_outgoing_webhook_retry_schedule_time(
    mapping: process_data::OutgoingWebhookRetryProcessTrackerMapping,
    merchant_id: &common_utils::id_type::MerchantId,
    retry_count: i32,
) -> Option<i32> {
    let retry_mapping = match mapping.custom_merchant_mapping.get(merchant_id) {
        Some(map) => map.clone(),
        None => mapping.default_mapping,
    };

    // For first try, get the `start_after` time
    if retry_count == 0 {
        Some(retry_mapping.start_after)
    } else {
        get_delay(retry_count, &retry_mapping.frequencies)
    }
}

pub fn get_pcr_payments_retry_schedule_time(
    mapping: process_data::RevenueRecoveryPaymentProcessTrackerMapping,
    merchant_id: &common_utils::id_type::MerchantId,
    retry_count: i32,
) -> Option<i32> {
    let mapping = match mapping.custom_merchant_mapping.get(merchant_id) {
        Some(map) => map.clone(),
        None => mapping.default_mapping,
    };
    // TODO: check if the current scheduled time is not more than the configured timerange

    // For first try, get the `start_after` time
    if retry_count == 0 {
        Some(mapping.start_after)
    } else {
        get_delay(retry_count, &mapping.frequencies)
    }
}

/// Get the delay based on the retry count
pub fn get_delay<'a>(
    retry_count: i32,
    frequencies: impl IntoIterator<Item = &'a (i32, i32)>,
) -> Option<i32> {
    // Preferably, fix this by using unsigned ints
    if retry_count <= 0 {
        return None;
    }

    let mut cumulative_count = 0;
    for &(frequency, count) in frequencies.into_iter() {
        cumulative_count += count;
        if cumulative_count >= retry_count {
            return Some(frequency);
        }
    }

    None
}

pub(crate) async fn lock_acquire_release<T, F, Fut>(
    state: &T,
    settings: &SchedulerSettings,
    callback: F,
) -> CustomResult<(), errors::ProcessTrackerError>
where
    F: Fn() -> Fut,
    T: SchedulerInterface + Send + Sync + ?Sized,
    Fut: futures::Future<Output = CustomResult<(), errors::ProcessTrackerError>>,
{
    let tag = "PRODUCER_LOCK";
    let lock_key = &settings.producer.lock_key;
    let lock_val = "LOCKED";
    let ttl = settings.producer.lock_ttl;

    if state
        .acquire_pt_lock(tag, lock_key, lock_val, ttl)
        .await
        .change_context(errors::ProcessTrackerError::ERedisError(
            errors::RedisError::RedisConnectionError.into(),
        ))?
    {
        let result = callback().await;
        state
            .release_pt_lock(tag, lock_key)
            .await
            .map_err(errors::ProcessTrackerError::ERedisError)?;
        result
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_delay() {
        let frequency_count = vec![(300, 10), (600, 5), (1800, 3), (3600, 2)];

        let retry_counts_and_expected_delays = [
            (-4, None),
            (-2, None),
            (0, None),
            (4, Some(300)),
            (7, Some(300)),
            (10, Some(300)),
            (12, Some(600)),
            (16, Some(1800)),
            (18, Some(1800)),
            (20, Some(3600)),
            (24, None),
            (30, None),
        ];

        for (retry_count, expected_delay) in retry_counts_and_expected_delays {
            let delay = get_delay(retry_count, &frequency_count);

            assert_eq!(
                delay, expected_delay,
                "Delay and expected delay differ for `retry_count` = {retry_count}"
            );
        }
    }
}


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


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

use actix_web::{web, HttpRequest, HttpResponse};
use api_models::process_tracker::revenue_recovery as revenue_recovery_api;
use router_env::Flow;

use crate::{
    core::{api_locking, revenue_recovery},
    routes::AppState,
    services::{api, authentication as auth, authorization::permissions::Permission},
};

pub async fn revenue_recovery_pt_retrieve_api(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> HttpResponse {
    let flow = Flow::RevenueRecoveryRetrieve;
    let id = path.into_inner();
    let payload = revenue_recovery_api::RevenueRecoveryId {
        revenue_recovery_id: id,
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _: (), id, _| {
            revenue_recovery::retrieve_revenue_recovery_process_tracker(
                state,
                id.revenue_recovery_id,
            )
        },
        &auth::JWTAuth {
            permission: Permission::ProfileRevenueRecoveryRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub fn entity_type(&self) -> EntityType {
                match self {
                    #(#entity_impl_per),*
                }
            }


pub struct CheckoutDisputeWebhookBody {
    #[serde(rename = "type")]
    pub transaction_type: CheckoutDisputeTransactionType,
    pub data: CheckoutDisputeWebhookData,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created_on: Option<PrimitiveDateTime>,
}


pub fn get_string_val(
        &self,
        params: &Vec<routing_types::DynamicRoutingConfigParams>,
    ) -> String {
        let mut parts: Vec<String> = Vec::new();
        for param in params {
            let val = match param {
                routing_types::DynamicRoutingConfigParams::PaymentMethod => self
                    .payment_method
                    .as_ref()
                    .map_or(String::new(), |pm| pm.to_string()),
                routing_types::DynamicRoutingConfigParams::PaymentMethodType => self
                    .payment_method_type
                    .as_ref()
                    .map_or(String::new(), |pmt| pmt.to_string()),
                routing_types::DynamicRoutingConfigParams::AuthenticationType => self
                    .authentication_type
                    .as_ref()
                    .map_or(String::new(), |at| at.to_string()),
                routing_types::DynamicRoutingConfigParams::Currency => self
                    .currency
                    .as_ref()
                    .map_or(String::new(), |cur| cur.to_string()),
                routing_types::DynamicRoutingConfigParams::Country => self
                    .country
                    .as_ref()
                    .map_or(String::new(), |cn| cn.to_string()),
                routing_types::DynamicRoutingConfigParams::CardNetwork => {
                    self.card_network.clone().unwrap_or_default()
                }
                routing_types::DynamicRoutingConfigParams::CardBin => {
                    self.card_bin.clone().unwrap_or_default()
                }
            };
            if !val.is_empty() {
                parts.push(val);
            }
        }
        parts.join(":")
    }


pub struct AuthEventMetricsBucketValue {
    pub authentication_count: Option<u64>,
    pub authentication_attempt_count: Option<u64>,
    pub authentication_success_count: Option<u64>,
    pub challenge_flow_count: Option<u64>,
    pub challenge_attempt_count: Option<u64>,
    pub challenge_success_count: Option<u64>,
    pub frictionless_flow_count: Option<u64>,
    pub frictionless_success_count: Option<u64>,
    pub error_message_count: Option<u64>,
    pub authentication_funnel: Option<u64>,
    pub authentication_exemption_approved_count: Option<u64>,
    pub authentication_exemption_requested_count: Option<u64>,
}


pub async fn receive_incoming_webhook<W: types::OutgoingWebhookType>(
    state: web::Data<AppState>,
    req: HttpRequest,
    body: web::Bytes,
    path: web::Path<(common_utils::id_type::MerchantId, String)


pub struct TokenizedBankDebitValue2 {
    pub customer_id: Option<id_type::CustomerId>,
}


impl fmt::Display for Order {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Ascending => write!(f, "asc"),
            Self::Descending => write!(f, "desc"),
        }
    }
}


File: crates/diesel_models/src/query/cards_info.rs
 Public functions: 3

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

use crate::{
    cards_info::{CardInfo, UpdateCardInfo},
    errors,
    query::generics,
    schema::cards_info::dsl,
    PgPooledConn, StorageResult,
};

impl CardInfo {
    pub async fn find_by_iin(conn: &PgPooledConn, card_iin: &str) -> StorageResult<Option<Self>> {
        generics::generic_find_by_id_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            card_iin.to_owned(),
        )
        .await
    }
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Self> {
        generics::generic_insert(conn, self).await
    }
    pub async fn update(
        conn: &PgPooledConn,
        card_iin: String,
        data: UpdateCardInfo,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, UpdateCardInfo, _, _>(
            conn,
            dsl::card_iin.eq(card_iin),
            data,
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(errors::DatabaseError::NotFound)
                .attach_printable("Error while updating card_info entry")
        })
    }
}


pub async fn create_jwe_body_for_vault(
    jwekey: &settings::Jwekey,
    jws: &str,
) -> CustomResult<encryption::JweBody, errors::VaultError> {
    let jws_payload: Vec<&str> = jws.split('.').collect();

    let generate_jws_body = |payload: Vec<&str>| -> Option<encryption::JwsBody> {
        Some(encryption::JwsBody {
            header: payload.first()?.to_string(),
            payload: payload.get(1)?.to_string(),
            signature: payload.get(2)?.to_string(),
        })
    };

    let jws_body =
        generate_jws_body(jws_payload).ok_or(errors::VaultError::RequestEncryptionFailed)?;

    let payload = jws_body
        .encode_to_vec()
        .change_context(errors::VaultError::RequestEncodingFailed)?;

    let public_key = jwekey.vault_encryption_key.peek().as_bytes();

    let jwe_encrypted =
        encryption::encrypt_jwe(&payload, public_key, EncryptionAlgorithm::A256GCM, None)
            .await
            .change_context(errors::VaultError::SaveCardFailed)
            .attach_printable("Error on jwe encrypt")?;
    let jwe_payload: Vec<&str> = jwe_encrypted.split('.').collect();

    let generate_jwe_body = |payload: Vec<&str>| -> Option<encryption::JweBody> {
        Some(encryption::JweBody {
            header: payload.first()?.to_string(),
            iv: payload.get(2)?.to_string(),
            encrypted_payload: payload.get(3)?.to_string(),
            tag: payload.get(4)?.to_string(),
            encrypted_key: payload.get(1)?.to_string(),
        })
    };

    let jwe_body =
        generate_jwe_body(jwe_payload).ok_or(errors::VaultError::RequestEncodingFailed)?;

    Ok(jwe_body)
}


pub struct StripePaymentListConstraints {
    pub customer: Option<id_type::CustomerId>,
    pub starting_after: Option<id_type::PaymentId>,
    pub ending_before: Option<id_type::PaymentId>,
    #[serde(default = "default_limit")]
    pub limit: u32,
    pub created: Option<i64>,
    #[serde(rename = "created[lt]")]
    pub created_lt: Option<i64>,
    #[serde(rename = "created[gt]")]
    pub created_gt: Option<i64>,
    #[serde(rename = "created[lte]")]
    pub created_lte: Option<i64>,
    #[serde(rename = "created[gte]")]
    pub created_gte: Option<i64>,
}


File: crates/api_models/src/analytics/connector_events.rs
 Public structs: 1

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct ConnectorEventsRequest {
    pub payment_id: common_utils::id_type::PaymentId,
    pub refund_id: Option<String>,
    pub dispute_id: Option<String>,
}


impl common_utils::events::ApiEventMetric for ThreeDsDecisionRuleExecuteResponse {
    fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
        Some(common_utils::events::ApiEventsType::ThreeDsDecisionRule)
    }
}

impl common_utils::events::ApiEventMetric for for ThreeDsDecisionRuleExecuteResponse


pub struct CustomerUpdateInternal {
    pub name: Option<Encryption>,
    pub email: Option<Encryption>,
    pub phone: Option<Encryption>,
    pub description: Option<Description>,
    pub phone_country_code: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: PrimitiveDateTime,
    pub connector_customer: Option<pii::SecretSerdeValue>,
    pub address_id: Option<String>,
    pub default_payment_method_id: Option<Option<String>>,
    pub updated_by: Option<String>,
    pub tax_registration_id: Option<Encryption>,
}


impl Responder {
        let flow = AnalyticsFlow::GetOutgoingWebhookEvents;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                utils::check_if_profile_id_is_present_in_payment_intent(
                    req.payment_id.clone(),
                    &state,
                    &auth,
                )
                .await
                .change_context(AnalyticsError::AccessForbiddenError)?;
                outgoing_webhook_events_core(&state.pool, req, auth.merchant_account.get_id())
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub struct NoonWebhookBody {
    pub order_id: u64,
    pub order_status: NoonPaymentStatus,
    pub event_type: NoonWebhookEventTypes,
    pub event_id: String,
    pub time_stamp: String,
}


impl api::RefundExecute for Dlocal {}


pub async fn decide_frm_flow(
    router_data: &mut FrmCheckoutRouterData,
    state: &SessionState,
    connector: &FraudCheckConnectorData,
    call_connector_action: payments::CallConnectorAction,
    _merchant_context: &domain::MerchantContext,
) -> RouterResult<FrmCheckoutRouterData> {
    let connector_integration: services::BoxedFrmConnectorIntegrationInterface<
        frm_api::Checkout,
        FraudCheckCheckoutData,
        FraudCheckResponseData,
    > = connector.connector.get_connector_integration();
    let resp = services::execute_connector_processing_step(
        state,
        connector_integration,
        router_data,
        call_connector_action,
        None,
        None,
    )
    .await
    .to_payment_failed_response()?;

    Ok(resp)
}


File: crates/injector/src/lib.rs

pub mod injector;
pub mod types;

// Re-export all functionality
pub use injector::*;
pub use types::*;


Documentation: api-reference/v1/blocklist/get-blocklist.mdx
# Type: Doc File

---
openapi: get /blocklist
---


    #[serde(rename = "618")]
    #[strum(serialize = "618")]
    HardDeclineIssueWithPayPalRefund,
    #[serde(rename = "619")]
    #[strum(serialize = "619")]
    HardDeclinePayPalCredentialsIssue,
    #[serde(rename = "620")]
    #[strum(serialize = "620")]
    HardDeclinePayPalAuthorizationVoidedOrExpired,
    #[serde(rename = "621")]
    #[strum(serialize = "621")]
    HardDeclineRequiredPayPalParameterMissing,
    #[serde(rename = "622")]
    #[strum(serialize = "622")]
    HardDeclinePayPalTransactionIdOrAuthIdIsInvalid,
    #[serde(rename = "623")]
    #[strum(serialize = "623")]
    HardDeclineExceededMaximumNumberOfPayPalAuthorizationAttempts,
    #[serde(rename = "624")]
    #[strum(serialize = "624")]
    HardDeclineTransactionAmountExceedsMerchantsPayPalAccountLimit,
    #[serde(rename = "625")]
    #[strum(serialize = "625")]
    HardDeclinePayPalFundingSourcesUnavailable,
    #[serde(rename = "626")]
    #[strum(serialize = "626")]
    HardDeclineIssueWithPayPalPrimaryFundingSource,
    #[serde(rename = "627")]
    #[strum(serialize = "627")]
    HardDeclinePayPalProfileDoesNotAllowThisTransactionType,
    #[serde(rename = "628")]
    #[strum(serialize = "628")]
    InternalSystemErrorWithPayPalContactVantiv,
    #[serde(rename = "629")]
    #[strum(serialize = "629")]
    HardDeclineContactPayPalConsumerForAnotherPaymentMethod,
    #[serde(rename = "637")]
    #[strum(serialize = "637")]
    InvalidTerminalId,
    #[serde(rename = "640")]
    #[strum(serialize = "640")]
    PinlessDebitProcessingNotSupportedForNonRecurringTransactions,
    #[serde(rename = "641")]
    #[strum(serialize = "641")]
    PinlessDebitProcessingNotSupportedForPartialAuths,
    #[serde(rename = "642")]
    #[strum(serialize = "642")]
    MerchantNotConfiguredForPinlessDebitProcessing,
    #[serde(rename = "651")]
    #[strum(serialize = "651")]
    DeclineCustomerCancellation,
    #[serde(rename = "652")]
    #[strum(serialize = "652")]
    DeclineReTryTransaction,
    #[serde(rename = "653")]
    #[strum(serialize = "653")]
    DeclineUnableToLocateRecordOnFile,
    #[serde(rename = "654")]
    #[strum(serialize = "654")]
    DeclineFileUpdateFieldEditError,
    #[serde(rename = "655")]
    #[strum(serialize = "655")]
    RemoteFunctionUnknown,
    #[serde(rename = "656")]
    #[strum(serialize = "656")]
    DeclinedExceedsWithdrawalFrequencyLimit,
    #[serde(rename = "657")]
    #[strum(serialize = "657")]
    DeclineCardRecordNotAvailable,
    #[serde(rename = "658")]
    #[strum(serialize = "658")]
    InvalidAuthorizationCode,
    #[serde(rename = "659")]
    #[strum(serialize = "659")]
    ReconciliationError,
    #[serde(rename = "660")]
    #[strum(serialize = "660")]
    PreferredDebitRoutingDenialCreditTransactionCanBeDebit,
    #[serde(rename = "661")]
    #[strum(serialize = "661")]
    DeclinedCurrencyConversionCompleteNoAuthPerformed,
    #[serde(rename = "662")]
    #[strum(serialize = "662")]
    DeclinedMultiCurrencyDccFail,
    #[serde(rename = "663")]
    #[strum(serialize = "663")]
    DeclinedMultiCurrencyInvertFail,
    #[serde(rename = "664")]
    #[strum(serialize = "664")]
    Invalid3DSecurePassword,
    #[serde(rename = "665")]
    #[strum(serialize = "665")]
    InvalidSocialSecurityNumber,
    #[serde(rename = "666")]
    #[strum(serialize = "666")]
    InvalidMothersMaidenName,
    #[serde(rename = "667")]
    #[strum(serialize = "667")]
    EnrollmentInquiryDeclined,
    #[serde(rename = "668")]
    #[strum(serialize = "668")]
    SocialSecurityNumberNotAvailable,
    #[serde(rename = "669")]
    #[strum(serialize = "669")]
    MothersMaidenNameNotAvailable,
    #[serde(rename = "670")]
    #[strum(serialize = "670")]
    PinAlreadyExistsOnDatabase,
    #[serde(rename = "701")]
    #[strum(serialize = "701")]
    Under18YearsOld,
    #[serde(rename = "702")]
    #[strum(serialize = "702")]
    BillToOutsideUsa,
    #[serde(rename = "703")]
    #[strum(serialize = "703")]
    BillToAddressIsNotEqualToShipToAddress,
    #[serde(rename = "704")]
    #[strum(serialize = "704")]
    DeclinedForeignCurrencyMustBeUsd,
    #[serde(rename = "705")]
    #[strum(serialize = "705")]
    OnNegativeFile,
    #[serde(rename = "706")]
    #[strum(serialize = "706")]
    BlockedAgreement,
    #[serde(rename = "707")]
    #[strum(serialize = "707")]
    InsufficientBuyingPower,
    #[serde(rename = "708")]
    #[strum(serialize = "708")]
    InvalidData,
    #[serde(rename = "709")]
    #[strum(serialize = "709")]
    InvalidDataDataElementsMissing,
    #[serde(rename = "710")]
    #[strum(serialize = "710")]
    InvalidDataDataFormatError,
    #[serde(rename = "711")]
    #[strum(serialize = "711")]
    InvalidDataInvalidTCVersion,
    #[serde(rename = "712")]
    #[strum(serialize = "712")]
    DuplicateTransactionPaypalCredit,
    #[serde(rename = "713")]
    #[strum(serialize = "713")]
    VerifyBillingAddress,
    #[serde(rename = "714")]
    #[strum(serialize = "714")]
    InactiveAccount,
    #[serde(rename = "716")]
    #[strum(serialize = "716")]
    InvalidAuth,
    #[serde(rename = "717")]
    #[strum(serialize = "717")]
    AuthorizationAlreadyExistsForTheOrder,
    #[serde(rename = "730")]
    #[strum(serialize = "730")]
    LodgingTransactionsAreNotAllowedForThisMcc,
    #[serde(rename = "731")]
    #[strum(serialize = "731")]
    DurationCannotBeNegative,
    #[serde(rename = "732")]
    #[strum(serialize = "732")]
    HotelFolioNumberCannotBeBlank,
    #[serde(rename = "733")]
    #[strum(serialize = "733")]
    InvalidCheckInDate,
    #[serde(rename = "734")]
    #[strum(serialize = "734")]
    InvalidCheckOutDate,
    #[serde(rename = "735")]
    #[strum(serialize = "735")]
    InvalidCheckInOrCheckOutDate,
    #[serde(rename = "736")]
    #[strum(serialize = "736")]
    CheckOutDateCannotBeBeforeCheckInDate,
    #[serde(rename = "737")]
    #[strum(serialize = "737")]
    NumberOfAdultsCannotBeNegative,
    #[serde(rename = "738")]
    #[strum(serialize = "738")]
    RoomRateCannotBeNegative,
    #[serde(rename = "739")]
    #[strum(serialize = "739")]
    RoomTaxCannotBeNegative,
    #[serde(rename = "740")]
    #[strum(serialize = "740")]
    DurationCanOnlyBeFrom0To99ForVisa,
    #[serde(rename = "801")]
    #[strum(serialize = "801")]
    AccountNumberWasSuccessfullyRegistered,
    #[serde(rename = "802")]
    #[strum(serialize = "802")]
    AccountNumberWasPreviouslyRegistered,
    #[serde(rename = "803")]
    #[strum(serialize = "803")]
    ValidToken,
    #[serde(rename = "820")]
    #[strum(serialize = "820")]
    CreditCardNumberWasInvalid,
    #[serde(rename = "821")]
    #[strum(serialize = "821")]
    MerchantIsNotAuthorizedForTokens,
    #[serde(rename = "822")]
    #[strum(serialize = "822")]
    TokenWasNotFound,
    #[serde(rename = "823")]
    #[strum(serialize = "823")]
    TokenInvalid,
    #[serde(rename = "825")]
    #[strum(serialize = "825")]
    MerchantNotAuthorizedForECheckTokens,
    #[serde(rename = "826")]
    #[strum(serialize = "826")]
    CheckoutIdWasInvalid,
    #[serde(rename = "827")]
    #[strum(serialize = "827")]
    CheckoutIdWasNotFound,
    #[serde(rename = "828")]
    #[strum(serialize = "828")]
    GenericCheckoutIdError,
    #[serde(rename = "835")]
    #[strum(serialize = "835")]
    CaptureAmountCanNotBeMoreThanAuthorizedAmount,
    #[serde(rename = "850")]
    #[strum(serialize = "850")]
    TaxBillingOnlyAllowedForMcc9311,
    #[serde(rename = "851")]
    #[strum(serialize = "851")]
    Mcc9311RequiresTaxTypeElement,
    #[serde(rename = "852")]
    #[strum(serialize = "852")]
    DebtRepaymentOnlyAllowedForViTransactionsOnMccs6012And6051,
    #[serde(rename = "861")]
    #[strum(serialize = "861")]
    RoutingNumberDidNotMatchOneOnFileForToken,
    #[serde(rename = "877")]
    #[strum(serialize = "877")]
    InvalidPayPageRegistrationId,
    #[serde(rename = "878")]
    #[strum(serialize = "878")]
    ExpiredPayPageRegistrationId,
    #[serde(rename = "879")]
    #[strum(serialize = "879")]
    MerchantIsNotAuthorizedForPayPage,
    #[serde(rename = "890")]
    #[strum(serialize = "890")]
    MaximumNumberOfUpdatesForThisTokenExceeded,
    #[serde(rename = "891")]
    #[strum(serialize = "891")]
    TooManyTokensCreatedForExistingNamespace,
    #[serde(rename = "895")]
    #[strum(serialize = "895")]
    PinValidationNotPossible,
    #[serde(rename = "898")]
    #[strum(serialize = "898")]
    GenericTokenRegistrationError,
    #[serde(rename = "899")]
    #[strum(serialize = "899")]
    GenericTokenUseError,
    #[serde(rename = "900")]
    #[strum(serialize = "900")]
    InvalidBankRoutingNumber,
    #[serde(rename = "901")]
    #[strum(serialize = "901")]
    MissingName,
    #[serde(rename = "902")]
    #[strum(serialize = "902")]
    InvalidName,
    #[serde(rename = "903")]
    #[strum(serialize = "903")]
    MissingBillingCountryCode,
    #[serde(rename = "904")]
    #[strum(serialize = "904")]
    InvalidIban,
    #[serde(rename = "905")]
    #[strum(serialize = "905")]
    MissingEmailAddress,
    #[serde(rename = "906")]
    #[strum(serialize = "906")]
    MissingMandateReference,
    #[serde(rename = "907")]
    #[strum(serialize = "907")]
    InvalidMandateReference,
    #[serde(rename = "908")]
    #[strum(serialize = "908")]
    MissingMandateUrl,
    #[serde(rename = "909")]
    #[strum(serialize = "909")]
    InvalidMandateUrl,
    #[serde(rename = "911")]
    #[strum(serialize = "911")]
    MissingMandateSignatureDate,
    #[serde(rename = "912")]
    #[strum(serialize = "912")]
    InvalidMandateSignatureDate,
    #[serde(rename = "913")]
    #[strum(serialize = "913")]
    RecurringMandateAlreadyExists,
    #[serde(rename = "914")]
    #[strum(serialize = "914")]
    RecurringMandateWasNotFound,
    #[serde(rename = "915")]
    #[strum(serialize = "915")]
    FinalRecurringWasAlreadyReceivedUsingThisMandate,
    #[serde(rename = "916")]
    #[strum(serialize = "916")]
    IbanDidNotMatchOneOnFileForMandate,
    #[serde(rename = "917")]
    #[strum(serialize = "917")]
    InvalidBillingCountry,
    #[serde(rename = "922")]
    #[strum(serialize = "922")]
    ExpirationDateRequiredForInteracTransaction,
    #[serde(rename = "923")]
    #[strum(serialize = "923")]
    TransactionTypeIsNotSupportedWithThisMethodOfPayment,
    #[serde(rename = "924")]
    #[strum(serialize = "924")]
    UnreferencedOrphanRefundsAreNotAllowed,
    #[serde(rename = "939")]
    #[strum(serialize = "939")]
    UnableToVoidATransactionWithAHeldState,
    #[serde(rename = "940")]
    #[strum(serialize = "940")]
    ThisFundingInstructionResultsInANegativeAccountBalance,
    #[serde(rename = "941")]
    #[strum(serialize = "941")]
    AccountBalanceInformationUnavailableAtThisTime,
    #[serde(rename = "942")]
    #[strum(serialize = "942")]
    TheSubmittedCardIsNotEligibleForFastAccessFunding,
    #[serde(rename = "943")]
    #[strum(serialize = "943")]
    TransactionCannotUseBothCcdPaymentInformationAndCtxPaymentInformation,
    #[serde(rename = "944")]
    #[strum(serialize = "944")]
    ProcessingError,
    #[serde(rename = "945")]
    #[strum(serialize = "945")]
    ThisFundingInstructionTypeIsInvalidForCanadianMerchants,
    #[serde(rename = "946")]
    #[strum(serialize = "946")]
    CtxAndCcdRecordsAreNotAllowedForCanadianMerchants,
    #[serde(rename = "947")]
    #[strum(serialize = "947")]
    CanadianAccountNumberCannotExceed12Digits,
    #[serde(rename = "948")]
    #[strum(serialize = "948")]
    ThisFundingInstructionTypeIsInvalid,
    #[serde(rename = "950")]
    #[strum(serialize = "950")]
    DeclineNegativeInformationOnFile,
    #[serde(rename = "951")]
    #[strum(serialize = "951")]
    AbsoluteDecline,
    #[serde(rename = "952")]
    #[strum(serialize = "952")]
    TheMerchantProfileDoesNotAllowTheRequestedOperation,
    #[serde(rename = "953")]
    #[strum(serialize = "953")]
    TheAccountCannotAcceptAchTransactions,
    #[serde(rename = "954")]
    #[strum(serialize = "954")]
    TheAccountCannotAcceptAchTransactionsOrSiteDrafts,
    #[serde(rename = "955")]
    #[strum(serialize = "955")]
    AmountGreaterThanLimitSpecifiedInTheMerchantProfile,
    #[serde(rename = "956")]
    #[strum(serialize = "956")]
    MerchantIsNotAuthorizedToPerformECheckVerificationTransactions,
    #[serde(rename = "957")]
    #[strum(serialize = "957")]
    FirstNameAndLastNameRequiredForECheckVerifications,
    #[serde(rename = "958")]
    #[strum(serialize = "958")]
    CompanyNameRequiredForCorporateAccountForECheckVerifications,
    #[serde(rename = "959")]
    #[strum(serialize = "959")]
    PhoneNumberRequiredForECheckVerifications,
    #[serde(rename = "961")]
    #[strum(serialize = "961")]
    CardBrandTokenNotSupported,
    #[serde(rename = "962")]
    #[strum(serialize = "962")]
    PrivateLabelCardNotSupported,
    #[serde(rename = "965")]
    #[strum(serialize = "965")]
    AllowedDailyDirectDebitCaptureECheckSaleLimitExceeded,
    #[serde(rename = "966")]
    #[strum(serialize = "966")]
    AllowedDailyDirectDebitCreditECheckCreditLimitExceeded,
    #[serde(rename = "973")]
    #[strum(serialize = "973")]
    AccountNotEligibleForRtp,
    #[serde(rename = "980")]
    #[strum(serialize = "980")]
    SoftDeclineCustomerAuthenticationRequired,
    #[serde(rename = "981")]
    #[strum(serialize = "981")]
    TransactionNotReversedVoidWorkflowNeedToBeInvoked,
    #[serde(rename = "982")]
    #[strum(serialize = "982")]
    TransactionReversalNotSupportedForTheCoreMerchants,
    #[serde(rename = "983")]
    #[strum(serialize = "983")]
    NoValidParentDepositOrParentRefundFound,
    #[serde(rename = "984")]
    #[strum(serialize = "984")]
    TransactionReversalNotEnabledForVisa,
    #[serde(rename = "985")]
    #[strum(serialize = "985")]
    TransactionReversalNotEnabledForMastercard,
    #[serde(rename = "986")]
    #[strum(serialize = "986")]
    TransactionReversalNotEnabledForAmEx,
    #[serde(rename = "987")]
    #[strum(serialize = "987")]
    TransactionReversalNotEnabledForDiscover,
    #[serde(rename = "988")]
    #[strum(serialize = "988")]
    TransactionReversalNotSupported,
    #[serde(rename = "990")]
    #[strum(serialize = "990")]
    FundingInstructionHeldPleaseContactYourRelationshipManager,
    #[serde(rename = "991")]
    #[strum(serialize = "991")]
    MissingAddressInformation,
    #[serde(rename = "992")]
    #[strum(serialize = "992")]
    CryptographicFailure,
    #[serde(rename = "993")]
    #[strum(serialize = "993")]
    InvalidRegionCode,
    #[serde(rename = "994")]
    #[strum(serialize = "994")]
    InvalidCountryCode,
    #[serde(rename = "995")]
    #[strum(serialize = "995")]
    InvalidCreditAccount,
    #[serde(rename = "996")]
    #[strum(serialize = "996")]
    InvalidCheckingAccount,
    #[serde(rename = "997")]
    #[strum(serialize = "997")]
    InvalidSavingsAccount,
    #[serde(rename = "998")]
    #[strum(serialize = "998")]
    InvalidUseOfMccCorrectAndReattempt,
    #[serde(rename = "999")]
    #[strum(serialize = "999")]
    ExceedsRtpTransactionLimit,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
#[serde(rename_all = "lowercase")]
pub enum WorldpayvantivPaymentFlow {
    Sale,
    Auth,
    Capture,
    Void,
    //VoidPostCapture
    VoidPC,
}

fn get_payment_flow_type(input: &str) -> Result<WorldpayvantivPaymentFlow, errors::ConnectorError> {
    if input.contains("auth") {
        Ok(WorldpayvantivPaymentFlow::Auth)
    } else if input.contains("sale") {
        Ok(WorldpayvantivPaymentFlow::Sale)
    } else if input.contains("voidpc") {
        Ok(WorldpayvantivPaymentFlow::VoidPC)
    } else if input.contains("void") {
        Ok(WorldpayvantivPaymentFlow::Void)
    } else if input.contains("capture") {
        Ok(WorldpayvantivPaymentFlow::Capture)
    } else {
        Err(errors::ConnectorError::UnexpectedResponseError(
            bytes::Bytes::from("Invalid merchantTxnId".to_string()),
        ))
    }
}

fn get_attempt_status(
    flow: WorldpayvantivPaymentFlow,
    response: WorldpayvantivResponseCode,
) -> Result<common_enums::AttemptStatus, errors::ConnectorError> {
    match response {
        WorldpayvantivResponseCode::Approved
            | WorldpayvantivResponseCode::PartiallyApproved
            | WorldpayvantivResponseCode::OfflineApproval
            | WorldpayvantivResponseCode::OfflineApprovalUnableToGoOnline
            | WorldpayvantivResponseCode::ConsumerNonReloadablePrepaidCardApproved
            | WorldpayvantivResponseCode::ConsumerSingleUseVirtualCardNumberApproved
            | WorldpayvantivResponseCode::ScheduledRecurringPaymentProcessed
            | WorldpayvantivResponseCode::ApprovedRecurringSubscriptionCreated
            | WorldpayvantivResponseCode::PendingShopperCheckoutCompletion
            | WorldpayvantivResponseCode::TransactionReceived
            | WorldpayvantivResponseCode::AccountNumberWasSuccessfullyRegistered
            | WorldpayvantivResponseCode::AccountNumberWasPreviouslyRegistered
            | WorldpayvantivResponseCode::ValidToken
             => match flow {
                WorldpayvantivPaymentFlow::Sale => Ok(common_enums::AttemptStatus::Pending),
                WorldpayvantivPaymentFlow::Auth => Ok(common_enums::AttemptStatus::Authorizing),
                WorldpayvantivPaymentFlow::Capture => Ok(common_enums::AttemptStatus::CaptureInitiated),
                WorldpayvantivPaymentFlow::Void => Ok(common_enums::AttemptStatus::VoidInitiated),
                WorldpayvantivPaymentFlow::VoidPC => {
                    Ok(common_enums::AttemptStatus::VoidInitiated)
                }
            },
        WorldpayvantivResponseCode::ShopperCheckoutExpired
            | WorldpayvantivResponseCode::ProcessingNetworkUnavailable
            | WorldpayvantivResponseCode::IssuerUnavailable
            | WorldpayvantivResponseCode::ReSubmitTransaction
            | WorldpayvantivResponseCode::TryAgainLater
            | WorldpayvantivResponseCode::InsufficientFunds
            | WorldpayvantivResponseCode::AuthorizationAmountHasAlreadyBeenDepleted
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter1Hour
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter24Hour
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter2Days
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter4Days
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter6Days
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter8Days
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter10Days
            | WorldpayvantivResponseCode::CallIssuer
            | WorldpayvantivResponseCode::CallAmex
            | WorldpayvantivResponseCode::CallDinersClub
            | WorldpayvantivResponseCode::CallDiscover
            | WorldpayvantivResponseCode::CallJbs
            | WorldpayvantivResponseCode::CallVisaMastercard
            | WorldpayvantivResponseCode::CallIssuerUpdateCardholderData
            | WorldpayvantivResponseCode::ExceedsApprovalAmountLimit
            | WorldpayvantivResponseCode::CallIndicatedNumber
            | WorldpayvantivResponseCode::UnacceptablePinTransactionDeclinedRetry
            | WorldpayvantivResponseCode::PinNotChanged
            | WorldpayvantivResponseCode::ConsumerMultiUseVirtualCardNumberSoftDecline
            | WorldpayvantivResponseCode::ConsumerNonReloadablePrepaidCardSoftDecline
            | WorldpayvantivResponseCode::ConsumerSingleUseVirtualCardNumberSoftDecline
            | WorldpayvantivResponseCode::UpdateCardholderData
            | WorldpayvantivResponseCode::MerchantDoesntQualifyForProductCode
            | WorldpayvantivResponseCode::Lifecycle
            | WorldpayvantivResponseCode::Policy
            | WorldpayvantivResponseCode::FraudSecurity
            | WorldpayvantivResponseCode::InvalidOrExpiredCardContactCardholderToUpdate
            | WorldpayvantivResponseCode::InvalidTransactionOrCardRestrictionVerifyInformationAndResubmit
            | WorldpayvantivResponseCode::AtLeastOneOfOrigIdOrOrigCnpTxnIdIsRequired
            | WorldpayvantivResponseCode::OrigCnpTxnIdIsRequiredWhenShowStatusOnlyIsUsed
            | WorldpayvantivResponseCode::IncrementalAuthNotSupported
            | WorldpayvantivResponseCode::SetAuthIndicatorToIncremental
            | WorldpayvantivResponseCode::IncrementalValueForAuthIndicatorNotAllowedInThisAuthStructure
            | WorldpayvantivResponseCode::CannotRequestAnIncrementalAuthIfOriginalAuthNotSetToEstimated
            | WorldpayvantivResponseCode::TransactionMustReferenceTheEstimatedAuth
            | WorldpayvantivResponseCode::IncrementedAuthExceedsMaxTransactionAmount
            | WorldpayvantivResponseCode::SubmittedMccNotAllowed
            | WorldpayvantivResponseCode::MerchantNotCertifiedEnabledForIias
            | WorldpayvantivResponseCode::IssuerGeneratedError
            | WorldpayvantivResponseCode::PickupCardOtherThanLostStolen
            | WorldpayvantivResponseCode::InvalidAmountHardDecline
            | WorldpayvantivResponseCode::ReversalUnsuccessful
            | WorldpayvantivResponseCode::MissingData
            | WorldpayvantivResponseCode::PickupCardLostCard
            | WorldpayvantivResponseCode::PickupCardStolenCard
            | WorldpayvantivResponseCode::RestrictedCard
            | WorldpayvantivResponseCode::InvalidDeactivate
            | WorldpayvantivResponseCode::CardAlreadyActive
            | WorldpayvantivResponseCode::CardNotActive
            | WorldpayvantivResponseCode::CardAlreadyDeactivate
            | WorldpayvantivResponseCode::OverMaxBalance
            | WorldpayvantivResponseCode::InvalidActivate
            | WorldpayvantivResponseCode::NoTransactionFoundForReversal
            | WorldpayvantivResponseCode::IncorrectCvv
            | WorldpayvantivResponseCode::IllegalTransaction
            | WorldpayvantivResponseCode::DuplicateTransaction
            | WorldpayvantivResponseCode::SystemError
            | WorldpayvantivResponseCode::DeconvertedBin
            | WorldpayvantivResponseCode::MerchantDepleted
            | WorldpayvantivResponseCode::GiftCardEscheated
            | WorldpayvantivResponseCode::InvalidReversalTypeForCreditCardTransaction
            | WorldpayvantivResponseCode::SystemErrorMessageFormatError
            | WorldpayvantivResponseCode::SystemErrorCannotProcess
            | WorldpayvantivResponseCode::RefundRejectedDueToPendingDepositStatus
            | WorldpayvantivResponseCode::RefundRejectedDueToDeclinedDepositStatus
            | WorldpayvantivResponseCode::RefundRejectedByTheProcessingNetwork
            | WorldpayvantivResponseCode::CaptureCreditAndAuthReversalTagsCannotBeUsedForGiftCardTransactions
            | WorldpayvantivResponseCode::InvalidAccountNumber
            | WorldpayvantivResponseCode::AccountNumberDoesNotMatchPaymentType
            | WorldpayvantivResponseCode::PickUpCard
            | WorldpayvantivResponseCode::LostStolenCard
            | WorldpayvantivResponseCode::ExpiredCard
            | WorldpayvantivResponseCode::AuthorizationHasExpiredNoNeedToReverse
            | WorldpayvantivResponseCode::RestrictedCardSoftDecline
            | WorldpayvantivResponseCode::RestrictedCardChargeback
            | WorldpayvantivResponseCode::RestrictedCardPrepaidCardFilteringService
            | WorldpayvantivResponseCode::InvalidTrackData
            | WorldpayvantivResponseCode::DepositIsAlreadyReferencedByAChargeback
            | WorldpayvantivResponseCode::RestrictedCardInternationalCardFilteringService
            | WorldpayvantivResponseCode::InternationalFilteringForIssuingCardCountry
            | WorldpayvantivResponseCode::RestrictedCardAuthFraudVelocityFilteringService
            | WorldpayvantivResponseCode::AutomaticRefundAlreadyIssued
            | WorldpayvantivResponseCode::RestrictedCardAuthFraudAdviceFilteringService
            | WorldpayvantivResponseCode::RestrictedCardFraudAvsFilteringService
            |  WorldpayvantivResponseCode::InvalidExpirationDate
            | WorldpayvantivResponseCode::InvalidMerchant
            | WorldpayvantivResponseCode::InvalidTransaction
            | WorldpayvantivResponseCode::NoSuchIssuer
            | WorldpayvantivResponseCode::InvalidPin
            | WorldpayvantivResponseCode::TransactionNotAllowedAtTerminal
            | WorldpayvantivResponseCode::ExceedsNumberOfPinEntries
            | WorldpayvantivResponseCode::CardholderTransactionNotPermitted
            | WorldpayvantivResponseCode::CardholderRequestedThatRecurringOrInstallmentPaymentBeStopped
            | WorldpayvantivResponseCode::InvalidPaymentType
            | WorldpayvantivResponseCode::InvalidPosCapabilityForCardholderAuthorizedTerminalTransaction
            | WorldpayvantivResponseCode::InvalidPosCardholderIdForCardholderAuthorizedTerminalTransaction
            | WorldpayvantivResponseCode::ThisMethodOfPaymentDoesNotSupportAuthorizationReversals
            | WorldpayvantivResponseCode::ReversalAmountDoesNotMatchAuthorizationAmount
            | WorldpayvantivResponseCode::TransactionDidNotConvertToPinless
            | WorldpayvantivResponseCode::InvalidAmountSoftDecline
            | WorldpayvantivResponseCode::InvalidHealthcareAmounts
            | WorldpayvantivResponseCode::InvalidBillingDescriptorPrefix
            | WorldpayvantivResponseCode::InvalidBillingDescriptor
            | WorldpayvantivResponseCode::InvalidReportGroup
            | WorldpayvantivResponseCode::DoNotHonor
            | WorldpayvantivResponseCode::GenericDecline
            | WorldpayvantivResponseCode::DeclineRequestPositiveId
            | WorldpayvantivResponseCode::DeclineCvv2CidFail
            | WorldpayvantivResponseCode::ThreeDSecureTransactionNotSupportedByMerchant
            | WorldpayvantivResponseCode::InvalidPurchaseLevelIiiTheTransactionContainedBadOrMissingData
            | WorldpayvantivResponseCode::MissingHealthcareIiasTagForAnFsaTransaction
            | WorldpayvantivResponseCode::RestrictedByVantivDueToSecurityCodeMismatch
            | WorldpayvantivResponseCode::NoTransactionFoundWithSpecifiedTransactionId
            | WorldpayvantivResponseCode::AuthorizationNoLongerAvailable
            | WorldpayvantivResponseCode::TransactionNotVoidedAlreadySettled
            | WorldpayvantivResponseCode::AutoVoidOnRefund
            | WorldpayvantivResponseCode::InvalidAccountNumberOriginalOrNocUpdatedECheckAccountRequired
            | WorldpayvantivResponseCode::TotalCreditAmountExceedsCaptureAmount
            | WorldpayvantivResponseCode::ExceedTheThresholdForSendingRedeposits
            | WorldpayvantivResponseCode::DepositHasNotBeenReturnedForInsufficientNonSufficientFunds
            | WorldpayvantivResponseCode::InvalidCheckNumber
            | WorldpayvantivResponseCode::RedepositAgainstInvalidTransactionType
            | WorldpayvantivResponseCode::InternalSystemErrorCallVantiv
            | WorldpayvantivResponseCode::OriginalTransactionHasBeenProcessedFutureRedepositsCanceled
            | WorldpayvantivResponseCode::SoftDeclineAutoRecyclingInProgress
            | WorldpayvantivResponseCode::HardDeclineAutoRecyclingComplete
            | WorldpayvantivResponseCode::RestrictedCardCardUnderSanction
            | WorldpayvantivResponseCode::MerchantIsNotEnabledForSurcharging
            | WorldpayvantivResponseCode::ThisMethodOfPaymentDoesNotSupportSurcharging
            | WorldpayvantivResponseCode::SurchargeIsNotValidForDebitOrPrepaidCards
            | WorldpayvantivResponseCode::SurchargeCannotExceedsTheMaximumAllowedLimit
            | WorldpayvantivResponseCode::TransactionDeclinedByTheProcessingNetwork
            | WorldpayvantivResponseCode::SecondaryAmountCannotExceedTheSaleAmount
            | WorldpayvantivResponseCode::ThisMethodOfPaymentDoesNotSupportSecondaryAmount
            | WorldpayvantivResponseCode::SecondaryAmountCannotBeLessThanZero
            | WorldpayvantivResponseCode::PartialTransactionIsNotSupportedWhenIncludingASecondaryAmount
            | WorldpayvantivResponseCode::SecondaryAmountRequiredOnPartialRefundWhenUsedOnDeposit
            | WorldpayvantivResponseCode::SecondaryAmountNotAllowedOnRefundIfNotIncludedOnDeposit
            | WorldpayvantivResponseCode::ProcessingNetworkError
            | WorldpayvantivResponseCode::InvalidEMail
            | WorldpayvantivResponseCode::InvalidCombinationOfAccountFundingTransactionTypeAndMcc
            | WorldpayvantivResponseCode::InvalidAccountFundingTransactionTypeForThisMethodOfPayment
            | WorldpayvantivResponseCode::MissingOneOrMoreReceiverFieldsForAccountFundingTransaction
            | WorldpayvantivResponseCode::InvalidRecurringRequestSeeRecurringResponseForDetails
            | WorldpayvantivResponseCode::ParentTransactionDeclinedRecurringSubscriptionNotCreated
            | WorldpayvantivResponseCode::InvalidPlanCode
            | WorldpayvantivResponseCode::InvalidSubscriptionId
            | WorldpayvantivResponseCode::AddOnCodeAlreadyExists
            | WorldpayvantivResponseCode::DuplicateAddOnCodesInRequests
            | WorldpayvantivResponseCode::NoMatchingAddOnCodeForTheSubscription
            | WorldpayvantivResponseCode::NoMatchingDiscountCodeForTheSubscription
            | WorldpayvantivResponseCode::DuplicateDiscountCodesInRequest
            | WorldpayvantivResponseCode::InvalidStartDate
            | WorldpayvantivResponseCode::MerchantNotRegisteredForRecurringEngine
            | WorldpayvantivResponseCode::InsufficientDataToUpdateSubscription
            | WorldpayvantivResponseCode::InvalidBillingDate
            | WorldpayvantivResponseCode::DiscountCodeAlreadyExists
            | WorldpayvantivResponseCode::PlanCodeAlreadyExists
            | WorldpayvantivResponseCode::TheAccountNumberWasChanged
            | WorldpayvantivResponseCode::TheAccountWasClosed
            | WorldpayvantivResponseCode::TheExpirationDateWasChanged
            | WorldpayvantivResponseCode::TheIssuingBankDoesNotParticipateInTheUpdateProgram
            | WorldpayvantivResponseCode::ContactTheCardholderForUpdatedInformation
            | WorldpayvantivResponseCode::TheCardholderHasOptedOutOfTheUpdateProgram
            | WorldpayvantivResponseCode::SoftDeclineCardReaderDecryptionServiceIsNotAvailable
            | WorldpayvantivResponseCode::SoftDeclineDecryptionFailed
            | WorldpayvantivResponseCode::HardDeclineInputDataIsInvalid
            | WorldpayvantivResponseCode::ApplePayKeyMismatch
            | WorldpayvantivResponseCode::ApplePayDecryptionFailed
            | WorldpayvantivResponseCode::HardDeclineDecryptionFailed
            | WorldpayvantivResponseCode::MerchantNotConfiguredForProcessingAtThisSite
            | WorldpayvantivResponseCode::AdvancedFraudFilterScoreBelowThreshold
            | WorldpayvantivResponseCode::SuspectedFraud
            | WorldpayvantivResponseCode::SystemErrorContactWorldpayRepresentative


File: crates/api_models/src/recon.rs
 Public structs: 4

use common_utils::{id_type, pii};
use masking::Secret;

use crate::enums;

#[derive(serde::Deserialize, Debug, serde::Serialize)]
pub struct ReconUpdateMerchantRequest {
    pub recon_status: enums::ReconStatus,
    pub user_email: pii::Email,
}

#[derive(Debug, serde::Serialize)]
pub struct ReconTokenResponse {
    pub token: Secret<String>,
}

#[derive(Debug, serde::Serialize)]
pub struct ReconStatusResponse {
    pub recon_status: enums::ReconStatus,
}

#[derive(serde::Serialize, Debug)]
pub struct VerifyTokenResponse {
    pub merchant_id: id_type::MerchantId,
    pub user_email: pii::Email,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub acl: Option<String>,
}


File: crates/hyperswitch_connectors/src/connectors/ctp_mastercard.rs
 Public structs: 1

use common_utils::errors::CustomResult;
use error_stack::report;
use hyperswitch_domain_models::{
    router_data::{AccessToken, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{PaymentsResponseData, RefundsResponseData},
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors, webhooks,
};

use crate::constants::headers;

#[derive(Clone)]
pub struct CtpMastercard;

impl api::Payment for CtpMastercard {}
impl api::PaymentSession for CtpMastercard {}
impl api::ConnectorAccessToken for CtpMastercard {}
impl api::MandateSetup for CtpMastercard {}
impl api::PaymentAuthorize for CtpMastercard {}
impl api::PaymentSync for CtpMastercard {}
impl api::PaymentCapture for CtpMastercard {}
impl api::PaymentVoid for CtpMastercard {}
impl api::Refund for CtpMastercard {}
impl api::RefundExecute for CtpMastercard {}
impl api::RefundSync for CtpMastercard {}
impl api::PaymentToken for CtpMastercard {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for CtpMastercard
{
    // Not Implemented (R)
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for CtpMastercard
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.get_content_type().to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }
}

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

    fn base_url<'a>(&self, _connectors: &'a Connectors) -> &'a str {
        ""
    }
}

impl ConnectorValidation for CtpMastercard {}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for CtpMastercard {}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for CtpMastercard {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for CtpMastercard
{
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData>
    for CtpMastercard
{
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for CtpMastercard {}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for CtpMastercard {}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for CtpMastercard {}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for CtpMastercard {}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for CtpMastercard {}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for CtpMastercard {
    fn get_webhook_object_reference_id(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

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

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

impl ConnectorSpecifications for CtpMastercard {}


pub struct ZenWebhookSignature {
    pub hash: String,
}


impl ApiEventMetric for AuthenticationAuthenticateRequest {
    fn get_api_event_type(&self) -> Option<ApiEventsType> {
        Some(ApiEventsType::Authentication {
            authentication_id: self.authentication_id.clone(),
        })
    }
}

impl ApiEventMetric for for AuthenticationAuthenticateRequest


Documentation: monitoring/README.md
# Type: Doc File

# Service Monitoring

## Components

[Promtail](https://grafana.com/docs/loki/latest/clients/promtail/#:~:text=Promtail%20is%20an%20agent%20which,Attaches%20labels%20to%20log%20streams)
: is a collector which ships the contents of local logs to a private Grafana Loki instance or Grafana Cloud. It is usually deployed to every machine that has applications needed to be monitored.

[Loki](https://grafana.com/docs/loki/latest/)
: is a search engine for logs inspired by Prometheus.

[OTEL Collector](https://opentelemetry.io/docs/collector/)
: is vendor-agnostic way to receive, process and export telemetry data.

[Tempo](https://grafana.com/docs/tempo/latest/)
: is a distributed tracing backend.

[Grafana](https://grafana.com/docs/grafana/latest/introduction/)
: is a query frontend to output data.

## How to run

```bash
cd monitoring

# start containers
docker-compose up -d

# FIXME: maybe we can remove manual setups with help of a config or automation?
```

### Set up logs monitor

1. Go to page of grafana: http://127.0.0.1:3000/
2. Use login and password "admin"
3. Add Loki data source: http://loki:3100
4. "Save & Exit" should give "Data source connected and labels found." if everything is okay.
5. Go to "Explore" tab and make a query "{job="varlogs"} |= ``".

### Set up logs monitor along with tracing

1. Navigate to [Grafana](http://localhost:3000/)
2. Enter "admin" for both username and password [skip if it asks for updating the password]
3. Add data source (Tempo)
   1. select `Tempo`
   2. set `URL` to `http://tempo:3200`
   3. save
4. Add data source (Loki)
   1. select Loki
   2. make it default
   3. set `URL` to `http://loki:3100`
   4. add `Derived fields`-
   5. set `Name` to `trace_id`
   6. set `Regex` to `trace_id":"(.*?)"(?=,|}|$)`
   7. set `URL` to `${__value.raw}`
   8. set `URL label` to `Tempo`
   9. enable `Internal link` and select `Tempo`
   10. save
5. Navigate to [Explore](http://localhost:3000/explore)
6. Add query [example, `job`=`router`]

### Notes:

- Use `trace_id` in logs to jump to Tempo view to visualize the tracing.
- Searching through recent trace ids is also possible by selecting appropriate `Service Name` in Tempo view under
  `Search` tab. The UI also provides other filter options.

## Helpful commands

`http://127.0.0.1:3100/ready`
: To get status of Loki, it should give "Ready".

`docker container ls -as`
: List running containers.

`docker exec -it monitoring_promtail_1 bash`
: Look inside of promtail container.

`docker-compose down`
: Stop containers


impl api::PaymentSession for Facilitapay {}

impl api::PaymentSession for for Facilitapay


pub struct ArchipelPaymentsCancelRequest {
    tenant_id: ArchipelTenantId,
}


pub async fn payment_intents_create(
    state: web::Data<routes::AppState>,
    qs_config: web::Data<serde_qs::Config>,
    req: HttpRequest,
    form_payload: web::Bytes,
) -> HttpResponse {
    let payload: types::StripePaymentIntentRequest = match qs_config
        .deserialize_bytes(&form_payload)
        .map_err(|err| report!(errors::StripeErrorCode::from(err)))
    {
        Ok(p) => p,
        Err(err) => return api::log_and_return_error_response(err),
    };

    tracing::Span::current().record(
        "payment_id",
        payload
            .id
            .as_ref()
            .map(|payment_id| payment_id.get_string_repr())
            .unwrap_or_default(),
    );

    logger::info!(tag = ?Tag::CompatibilityLayerRequest, payload = ?payload);

    let mut create_payment_req: payment_types::PaymentsRequest = match payload.try_into() {
        Ok(req) => req,
        Err(err) => return api::log_and_return_error_response(err),
    };

    if let Err(err) = get_or_generate_payment_id(&mut create_payment_req) {
        return api::log_and_return_error_response(err);
    }
    let flow = Flow::PaymentsCreate;
    let locking_action = create_payment_req.get_locking_input(flow.clone());
    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::StripePaymentIntentResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        create_payment_req,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            let eligible_connectors = req.connector.clone();
            payments::payments_core::<
                api_types::Authorize,
                api_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Authorize>,
            >(
                state,
                req_state,
                merchant_context,
                None,
                payments::PaymentCreate,
                req,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                eligible_connectors,
                hyperswitch_domain_models::payments::HeaderPayload::default(),
            )
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        locking_action,
    ))
    .await
}


impl std::error::Error for InvalidFileStorageConfig {}

impl std::error::Error for for InvalidFileStorageConfig


pub struct RecipientCreateAddress {
    pub street: String,
    pub city: String,
    pub postal_code: String,
    pub country: CountryAlpha2,
}


impl api::RefundExecute for UnifiedAuthenticationService {}

impl api::RefundExecute for for UnifiedAuthenticationService


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


pub struct PaymentMethodsUpdateForm {
    #[multipart(limit = "1MB")]
    pub file: bytes::Bytes,

    pub merchant_id: text::Text<id_type::MerchantId>,
}


pub struct DeliveryObject {
    first_name: Secret<String>,
    last_name: Secret<String>,
    address1: Secret<String>,
    house_number: Secret<String>,
    zip_code: Secret<String>,
    city: String,
    country: api_models::enums::CountryAlpha2,
}


pub async fn sync_merchant_onboarding_status(
    state: SessionState,
    tracking_id: String,
) -> RouterResult<api::OnboardingStatus> {
    let access_token = utils::paypal::generate_access_token(state.clone()).await?;

    let Some(seller_status_response) =
        find_paypal_merchant_by_tracking_id(state.clone(), tracking_id, &access_token).await?
    else {
        return Ok(api::OnboardingStatus::PayPal(
            api::PayPalOnboardingStatus::AccountNotFound,
        ));
    };

    let merchant_details_url = seller_status_response
        .extract_merchant_details_url(&state.conf.connectors.paypal.base_url)?;

    let merchant_details_request =
        utils::paypal::build_paypal_get_request(merchant_details_url, access_token.token.expose())?;

    let merchant_details_response =
        http_client::send_request(&state.conf.proxy, merchant_details_request, None)
            .await
            .change_context(ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to send request to paypal merchant details")?;

    let parsed_response: types::paypal::SellerStatusDetailsResponse = merchant_details_response
        .json()
        .await
        .change_context(ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to parse paypal merchant details response")?;

    let eligibity = parsed_response.get_eligibility_status().await?;
    Ok(api::OnboardingStatus::PayPal(eligibity))
}