File: crates/test_utils/tests/connectors/airwallex_ui.rs

use serial_test::serial;
use thirtyfour::{prelude::*, WebDriver};

use crate::{selenium::*, tester};

struct AirwallexSeleniumTest;

impl SeleniumTest for AirwallexSeleniumTest {
    fn get_connector_name(&self) -> String {
        "airwallex".to_string()
    }
}

async fn should_make_airwallex_3ds_payment(web_driver: WebDriver) -> Result<(), WebDriverError> {
    let conn = AirwallexSeleniumTest {};
    conn.make_redirection_payment(
        web_driver,
        vec![
            Event::Trigger(Trigger::Goto(&format!("{CHECKOUT_BASE_URL}/saved/85"))),
            Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
            Event::Trigger(Trigger::Query(By::ClassName("title"))),
            Event::Assert(Assert::Eq(
                Selector::Title,
                "Airwallex - Create 3D Secure Payment",
            )),
            Event::Trigger(Trigger::SendKeys(By::Id("challengeDataEntry"), "1234")),
            Event::Trigger(Trigger::Click(By::Id("submit"))),
            Event::Assert(Assert::IsPresent("Google")),
            Event::Assert(Assert::Contains(
                Selector::QueryParamStr,
                "status=succeeded",
            )),
        ],
    )
    .await?;
    Ok(())
}

async fn should_make_airwallex_gpay_payment(web_driver: WebDriver) -> Result<(), WebDriverError> {
    let conn = AirwallexSeleniumTest {};
    let merchant_name = conn
        .get_configs()
        .automation_configs
        .unwrap()
        .airwallex_merchant_name
        .unwrap();
    conn.make_gpay_payment(web_driver,
        &format!("{CHECKOUT_BASE_URL}/gpay?gatewayname=airwallex&gatewaymerchantid={merchant_name}&amount=70.00&country=US&currency=USD"),
        vec![
            Event::Trigger(Trigger::Query(By::ClassName("title"))),
            Event::Assert(Assert::Eq(Selector::Title, "Airwallex - Create 3D Secure Payment")),
            Event::Trigger(Trigger::SendKeys(By::Id("challengeDataEntry"), "1234")),
            Event::Trigger(Trigger::Click(By::Id("submit"))),
            Event::Assert(Assert::IsPresent("succeeded")),
    ]).await?;
    Ok(())
}

#[test]
#[serial]
fn should_make_airwallex_3ds_payment_test() {
    tester!(should_make_airwallex_3ds_payment);
}

#[test]
#[serial]
#[ignore]
fn should_make_airwallex_gpay_payment_test() {
    tester!(should_make_airwallex_gpay_payment);
}


impl api::RefundSync for Nordea {}


File: crates/hyperswitch_connectors/src/lib.rs

//! Hyperswitch connectors

pub mod connectors;
pub mod constants;
pub mod default_implementations;
pub mod default_implementations_v2;
pub mod metrics;
pub mod types;
pub mod utils;


pub async fn generate_jwt<T>(
    claims_data: &T,
    settings: &Settings,
) -> CustomResult<String, UserErrors>
where
    T: serde::ser::Serialize,
{
    let jwt_secret = &settings.secrets.get_inner().jwt_secret;
    encode(
        &Header::default(),
        claims_data,
        &EncodingKey::from_secret(jwt_secret.peek().as_bytes()),
    )
    .change_context(UserErrors::InternalServerError)
}


OpenAPI Block Path: components.schemas.PriorityLogicOutput
{
  "type": "object",
  "properties": {
    "isEnforcement": {
      "type": "boolean",
      "nullable": true
    },
    "gws": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "nullable": true
    },
    "priorityLogicTag": {
      "type": "string",
      "nullable": true
    },
    "gatewayReferenceIds": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      },
      "nullable": true
    },
    "primaryLogic": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PriorityLogicData"
        }
      ],
      "nullable": true
    },
    "fallbackLogic": {
      "allOf": [
        {
          "$ref": "#/components/schemas/PriorityLogicData"
        }
      ],
      "nullable": true
    }
  }
}


pub fn get_new_organization(&self) -> NewUserOrganization {
        self.new_organization.clone()
    }


pub struct Ebanx {
    #[cfg(feature = "payouts")]
    amount_converter: &'static (dyn AmountConvertor<Output = FloatMajorUnit> + Sync),
}


pub fn update_enabled_features(&mut self, feature_to_enable: DynamicRoutingFeatures) {
        self.enabled_feature = feature_to_enable
    }


pub struct ChargebeeInvoiceData {
    // invoice id
    pub id: String,
    pub total: MinorUnit,
    pub currency_code: enums::Currency,
    pub billing_address: Option<ChargebeeInvoiceBillingAddress>,
    pub linked_payments: Option<Vec<ChargebeeInvoicePayments>>,
}


OpenAPI Block Path: components.schemas.WebhookDeliveryAttempt
{
  "type": "string",
  "enum": [
    "initial_attempt",
    "automatic_retry",
    "manual_retry"
  ]
}


pub fn get_all_payout_keys() -> JsResult {
    let keys: Vec<&'static str> = dir::PayoutDirKeyKind::VARIANTS.to_vec();
    Ok(serde_wasm_bindgen::to_value(&keys)?)
}


pub struct AuthResult {
    pub authentication_result: PaypalThreeDsParams,
}


File: crates/common_utils/src/id_type/profile_acquirer.rs

use std::str::FromStr;

crate::id_type!(
    ProfileAcquirerId,
    "A type for profile_acquirer_id that can be used for profile acquirer ids"
);
crate::impl_id_type_methods!(ProfileAcquirerId, "profile_acquirer_id");

// This is to display the `ProfileAcquirerId` as ProfileAcquirerId(abcd)
crate::impl_debug_id_type!(ProfileAcquirerId);
crate::impl_try_from_cow_str_id_type!(ProfileAcquirerId, "profile_acquirer_id");

crate::impl_generate_id_id_type!(ProfileAcquirerId, "pro_acq");
crate::impl_serializable_secret_id_type!(ProfileAcquirerId);
crate::impl_queryable_id_type!(ProfileAcquirerId);
crate::impl_to_sql_from_sql_id_type!(ProfileAcquirerId);

impl Ord for ProfileAcquirerId {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0 .0 .0.cmp(&other.0 .0 .0)
    }
}

impl PartialOrd for ProfileAcquirerId {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl crate::events::ApiEventMetric for ProfileAcquirerId {
    fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
        Some(crate::events::ApiEventsType::ProfileAcquirer {
            profile_acquirer_id: self.clone(),
        })
    }
}

impl FromStr for ProfileAcquirerId {
    type Err = error_stack::Report<crate::errors::ValidationError>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let cow_string = std::borrow::Cow::Owned(s.to_string());
        Self::try_from(cow_string)
    }
}

// This is implemented so that we can use profile acquirer id directly as attribute in metrics
#[cfg(feature = "metrics")]
impl From<ProfileAcquirerId> for router_env::opentelemetry::Value {
    fn from(val: ProfileAcquirerId) -> Self {
        Self::from(val.0 .0 .0)
    }
}


File: crates/router/src/core/webhooks/outgoing_v2.rs

use std::collections::HashMap;

use api_models::{webhook_events, webhooks};
use common_utils::{ext_traits, request, type_name, types::keymanager};
use diesel_models::process_tracker::business_status;
use error_stack::{report, Report, ResultExt};
use hyperswitch_domain_models::type_encryption::{crypto_operation, CryptoOperation};
use hyperswitch_interfaces::consts;
use masking;
use router_env::{
    instrument,
    tracing::{self, Instrument},
};

use super::{
    types,
    utils::{self, increment_webhook_outgoing_received_count},
    MERCHANT_ID,
};
use crate::{
    core::{
        errors::{self, CustomResult},
        metrics,
    },
    events::outgoing_webhook_logs,
    logger,
    routes::{app::SessionStateInfo, SessionState},
    services,
    types::{
        api, domain,
        storage::{self, enums},
        transformers::ForeignFrom,
    },
};

#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub(crate) async fn create_event_and_trigger_outgoing_webhook(
    state: SessionState,
    business_profile: domain::Profile,
    merchant_key_store: &domain::MerchantKeyStore,
    event_type: enums::EventType,
    event_class: enums::EventClass,
    primary_object_id: String,
    primary_object_type: enums::EventObjectType,
    content: api::OutgoingWebhookContent,
    primary_object_created_at: time::PrimitiveDateTime,
) -> CustomResult<(), errors::ApiErrorResponse> {
    let delivery_attempt = enums::WebhookDeliveryAttempt::InitialAttempt;
    let idempotent_event_id =
        utils::get_idempotent_event_id(&primary_object_id, event_type, delivery_attempt);
    let webhook_url_result = business_profile
        .get_webhook_url_from_profile()
        .change_context(errors::WebhooksFlowError::MerchantWebhookUrlNotConfigured);

    if utils::is_outgoing_webhook_disabled(
        &state,
        &webhook_url_result,
        &business_profile,
        &idempotent_event_id,
    ) {
        return Ok(());
    }

    let event_id = utils::generate_event_id();
    let merchant_id = business_profile.merchant_id.clone();
    let now = common_utils::date_time::now();

    let outgoing_webhook = api::OutgoingWebhook {
        merchant_id: merchant_id.clone(),
        event_id: event_id.clone(),
        event_type,
        content: content.clone(),
        timestamp: now,
    };

    let request_content = get_outgoing_webhook_request(outgoing_webhook, &business_profile)
        .change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
        .attach_printable("Failed to construct outgoing webhook request content")?;

    let event_metadata = storage::EventMetadata::foreign_from(&content);
    let key_manager_state = &(&state).into();
    let new_event = domain::Event {
        event_id: event_id.clone(),
        event_type,
        event_class,
        is_webhook_notified: false,
        primary_object_id,
        primary_object_type,
        created_at: now,
        merchant_id: Some(business_profile.merchant_id.clone()),
        business_profile_id: Some(business_profile.get_id().to_owned()),
        primary_object_created_at: Some(primary_object_created_at),
        idempotent_event_id: Some(idempotent_event_id.clone()),
        initial_attempt_id: Some(event_id.clone()),
        request: Some(
            crypto_operation(
                key_manager_state,
                type_name!(domain::Event),
                CryptoOperation::Encrypt(
                    ext_traits::Encode::encode_to_string_of_json(&request_content)
                        .change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
                        .attach_printable("Failed to encode outgoing webhook request content")
                        .map(masking::Secret::new)?,
                ),
                keymanager::Identifier::Merchant(merchant_key_store.merchant_id.clone()),
                masking::PeekInterface::peek(merchant_key_store.key.get_inner()),
            )
            .await
            .and_then(|val| val.try_into_operation())
            .change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
            .attach_printable("Failed to encrypt outgoing webhook request content")?,
        ),
        response: None,
        delivery_attempt: Some(delivery_attempt),
        metadata: Some(event_metadata),
        is_overall_delivery_successful: Some(false),
    };

    let event_insert_result = state
        .store
        .insert_event(key_manager_state, new_event, merchant_key_store)
        .await;

    let event = match event_insert_result {
        Ok(event) => Ok(event),
        Err(error) => {
            if error.current_context().is_db_unique_violation() {
                // If the event_id already exists in the database, it indicates that the event for the resource has already been sent, so we skip the flow
                logger::debug!("Event with idempotent ID `{idempotent_event_id}` already exists in the database");
                return Ok(());
            } else {
                logger::error!(event_insertion_failure=?error);
                Err(error
                    .change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
                    .attach_printable("Failed to insert event in events table"))
            }
        }
    }?;

    let cloned_key_store = merchant_key_store.clone();
    // Using a tokio spawn here and not arbiter because not all caller of this function
    // may have an actix arbiter
    tokio::spawn(
        async move {
            Box::pin(trigger_webhook_and_raise_event(
                state,
                business_profile,
                &cloned_key_store,
                event,
                request_content,
                delivery_attempt,
                Some(content),
            ))
            .await;
        }
        .in_current_span(),
    );

    Ok(())
}

#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub(crate) async fn trigger_webhook_and_raise_event(
    state: SessionState,
    business_profile: domain::Profile,
    merchant_key_store: &domain::MerchantKeyStore,
    event: domain::Event,
    request_content: webhook_events::OutgoingWebhookRequestContent,
    delivery_attempt: enums::WebhookDeliveryAttempt,
    content: Option<api::OutgoingWebhookContent>,
) {
    logger::debug!(
        event_id=%event.event_id,
        idempotent_event_id=?event.idempotent_event_id,
        initial_attempt_id=?event.initial_attempt_id,
        "Attempting to send webhook"
    );

    let merchant_id = business_profile.merchant_id.clone();
    let trigger_webhook_result = trigger_webhook_to_merchant(
        state.clone(),
        business_profile,
        merchant_key_store,
        event.clone(),
        request_content,
        delivery_attempt,
    )
    .await;

    let _ =
        raise_webhooks_analytics_event(state, trigger_webhook_result, content, merchant_id, event)
            .await;
}

async fn trigger_webhook_to_merchant(
    state: SessionState,
    business_profile: domain::Profile,
    merchant_key_store: &domain::MerchantKeyStore,
    event: domain::Event,
    request_content: webhook_events::OutgoingWebhookRequestContent,
    delivery_attempt: enums::WebhookDeliveryAttempt,
) -> CustomResult<
    (domain::Event, Option<Report<errors::WebhooksFlowError>>),
    errors::WebhooksFlowError,
> {
    let webhook_url = business_profile
        .get_webhook_url_from_profile()
        .change_context(errors::WebhooksFlowError::MerchantWebhookUrlNotConfigured)?;

    let response = build_and_send_request(&state, request_content, webhook_url).await;

    metrics::WEBHOOK_OUTGOING_COUNT.add(
        1,
        router_env::metric_attributes!((MERCHANT_ID, business_profile.merchant_id.clone())),
    );
    logger::debug!(outgoing_webhook_response=?response);

    match response {
        Ok(response) => {
            delivery_attempt
                .handle_success_response(
                    state,
                    merchant_key_store.clone(),
                    &business_profile.merchant_id,
                    &event.event_id,
                    None,
                    response,
                )
                .await
        }
        Err(client_error) => {
            delivery_attempt
                .handle_error_response(
                    state,
                    merchant_key_store.clone(),
                    &business_profile.merchant_id,
                    &event.event_id,
                    client_error,
                )
                .await
        }
    }
}

async fn raise_webhooks_analytics_event(
    state: SessionState,
    trigger_webhook_result: CustomResult<
        (domain::Event, Option<Report<errors::WebhooksFlowError>>),
        errors::WebhooksFlowError,
    >,
    content: Option<api::OutgoingWebhookContent>,
    merchant_id: common_utils::id_type::MerchantId,
    fallback_event: domain::Event,
) {
    let (updated_event, optional_error) = match trigger_webhook_result {
        Ok((updated_event, error)) => (updated_event, error),
        Err(error) => (fallback_event, Some(error)),
    };
    let error = optional_error.and_then(|error| {
        logger::error!(?error, "Failed to send webhook to merchant");

        serde_json::to_value(error.current_context())
            .change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
            .inspect_err(|error| {
                logger::error!(?error, "Failed to serialize outgoing webhook error as JSON");
            })
            .ok()
    });

    let outgoing_webhook_event_content = content
        .as_ref()
        .and_then(
            outgoing_webhook_logs::OutgoingWebhookEventMetric::get_outgoing_webhook_event_content,
        )
        .or_else(|| {
            updated_event
                .metadata
                .map(outgoing_webhook_logs::OutgoingWebhookEventContent::foreign_from)
        });

    // Get status_code from webhook response
    let status_code = {
        let webhook_response: Option<webhook_events::OutgoingWebhookResponseContent> =
            updated_event.response.and_then(|res| {
                ext_traits::StringExt::parse_struct(
                    masking::PeekInterface::peek(res.get_inner()),
                    "OutgoingWebhookResponseContent",
                )
                .map_err(|error| {
                    logger::error!(?error, "Error deserializing webhook response");
                    error
                })
                .ok()
            });
        webhook_response.and_then(|res| res.status_code)
    };

    let webhook_event = outgoing_webhook_logs::OutgoingWebhookEvent::new(
        state.tenant.tenant_id.clone(),
        merchant_id,
        updated_event.event_id,
        updated_event.event_type,
        outgoing_webhook_event_content,
        error,
        updated_event.initial_attempt_id,
        status_code,
        updated_event.delivery_attempt,
    );
    state.event_handler().log_event(&webhook_event);
}

pub(crate) fn get_outgoing_webhook_request(
    outgoing_webhook: api::OutgoingWebhook,
    business_profile: &domain::Profile,
) -> CustomResult<webhook_events::OutgoingWebhookRequestContent, errors::WebhooksFlowError> {
    #[inline]
    fn get_outgoing_webhook_request_inner<WebhookType: types::OutgoingWebhookType>(
        outgoing_webhook: api::OutgoingWebhook,
        business_profile: &domain::Profile,
    ) -> CustomResult<webhook_events::OutgoingWebhookRequestContent, errors::WebhooksFlowError>
    {
        let mut headers = vec![
            (
                reqwest::header::CONTENT_TYPE.to_string(),
                mime::APPLICATION_JSON.essence_str().into(),
            ),
            (
                reqwest::header::USER_AGENT.to_string(),
                consts::USER_AGENT.to_string().into(),
            ),
        ];

        let transformed_outgoing_webhook = WebhookType::from(outgoing_webhook);
        let payment_response_hash_key = business_profile.payment_response_hash_key.clone();
        let custom_headers = business_profile
            .outgoing_webhook_custom_http_headers
            .clone()
            .map(|headers| {
                ext_traits::ValueExt::parse_value::<HashMap<String, String>>(
                    masking::ExposeInterface::expose(headers.into_inner()),
                    "HashMap<String,String>",
                )
                .change_context(errors::WebhooksFlowError::OutgoingWebhookEncodingFailed)
                .attach_printable("Failed to deserialize outgoing webhook custom HTTP headers")
            })
            .transpose()?;
        if let Some(ref map) = custom_headers {
            headers.extend(
                map.iter()
                    .map(|(key, value)| (key.clone(), masking::Mask::into_masked(value.clone()))),
            );
        };
        let outgoing_webhooks_signature = transformed_outgoing_webhook
            .get_outgoing_webhooks_signature(payment_response_hash_key)?;

        if let Some(signature) = outgoing_webhooks_signature.signature {
            WebhookType::add_webhook_header(&mut headers, signature)
        }

        Ok(webhook_events::OutgoingWebhookRequestContent {
            body: outgoing_webhooks_signature.payload,
            headers: headers
                .into_iter()
                .map(|(name, value)| (name, masking::Secret::new(value.into_inner())))
                .collect(),
        })
    }

    get_outgoing_webhook_request_inner::<webhooks::OutgoingWebhook>(
        outgoing_webhook,
        business_profile,
    )
}

async fn build_and_send_request(
    state: &SessionState,
    request_content: webhook_events::OutgoingWebhookRequestContent,
    webhook_url: String,
) -> Result<reqwest::Response, Report<common_enums::ApiClientError>> {
    let headers = request_content
        .headers
        .into_iter()
        .map(|(name, value)| (name, masking::Mask::into_masked(value)))
        .collect();
    let request = services::RequestBuilder::new()
        .method(services::Method::Post)
        .url(&webhook_url)
        .attach_default_headers()
        .headers(headers)
        .set_body(request::RequestContent::RawBytes(
            masking::ExposeInterface::expose(request_content.body).into_bytes(),
        ))
        .build();

    state
        .api_client
        .send_request(state, request, None, false)
        .await
}

async fn api_client_error_handler(
    state: SessionState,
    merchant_key_store: domain::MerchantKeyStore,
    merchant_id: &common_utils::id_type::MerchantId,
    event_id: &str,
    client_error: Report<errors::ApiClientError>,
    delivery_attempt: enums::WebhookDeliveryAttempt,
    _schedule_webhook_retry: types::ScheduleWebhookRetry,
) -> CustomResult<
    (domain::Event, Option<Report<errors::WebhooksFlowError>>),
    errors::WebhooksFlowError,
> {
    // Not including detailed error message in response information since it contains too
    // much of diagnostic information to be exposed to the merchant.
    let is_webhook_notified = false;
    let response_to_store = webhook_events::OutgoingWebhookResponseContent {
        body: None,
        headers: None,
        status_code: None,
        error_message: Some("Unable to send request to merchant server".to_string()),
    };
    let updated_event = update_event_in_storage(
        state,
        is_webhook_notified,
        response_to_store,
        merchant_key_store,
        merchant_id,
        event_id,
    )
    .await?;

    let error = client_error.change_context(errors::WebhooksFlowError::CallToMerchantFailed);
    logger::error!(
        ?error,
        ?delivery_attempt,
        "An error occurred when sending webhook to merchant"
    );

    //TODO: add outgoing webhook retries support
    // if let ScheduleWebhookRetry::WithProcessTracker(process_tracker) = schedule_webhook_retry {
    //     // Schedule a retry attempt for webhook delivery
    //     outgoing_webhook_retry::retry_webhook_delivery_task(
    //         &*state.store,
    //         merchant_id,
    //         *process_tracker,
    //     )
    //     .await
    //     .change_context(errors::WebhooksFlowError::OutgoingWebhookRetrySchedulingFailed)?;
    // }

    Ok((updated_event, Some(error)))
}

async fn update_event_in_storage(
    state: SessionState,
    is_webhook_notified: bool,
    outgoing_webhook_response: webhook_events::OutgoingWebhookResponseContent,
    merchant_key_store: domain::MerchantKeyStore,
    merchant_id: &common_utils::id_type::MerchantId,
    event_id: &str,
) -> CustomResult<domain::Event, errors::WebhooksFlowError> {
    let key_manager_state = &(&state).into();
    let event_update = domain::EventUpdate::UpdateResponse {
        is_webhook_notified,
        response: Some(
            crypto_operation(
                key_manager_state,
                type_name!(domain::Event),
                CryptoOperation::Encrypt(
                    ext_traits::Encode::encode_to_string_of_json(&outgoing_webhook_response)
                        .change_context(
                            errors::WebhooksFlowError::OutgoingWebhookResponseEncodingFailed,
                        )
                        .map(masking::Secret::new)?,
                ),
                keymanager::Identifier::Merchant(merchant_key_store.merchant_id.clone()),
                masking::PeekInterface::peek(merchant_key_store.key.get_inner()),
            )
            .await
            .and_then(|val| val.try_into_operation())
            .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed)
            .attach_printable("Failed to encrypt outgoing webhook response content")?,
        ),
    };
    state
        .store
        .update_event_by_merchant_id_event_id(
            key_manager_state,
            merchant_id,
            event_id,
            event_update,
            &merchant_key_store,
        )
        .await
        .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed)
}

async fn update_overall_delivery_status_in_storage(
    state: SessionState,
    merchant_key_store: domain::MerchantKeyStore,
    merchant_id: &common_utils::id_type::MerchantId,
    updated_event: &domain::Event,
) -> CustomResult<(), errors::WebhooksFlowError> {
    let key_manager_state = &(&state).into();

    let update_overall_delivery_status = domain::EventUpdate::OverallDeliveryStatusUpdate {
        is_overall_delivery_successful: true,
    };

    let initial_attempt_id = updated_event.initial_attempt_id.as_ref();
    let delivery_attempt = updated_event.delivery_attempt;

    if let Some((
        initial_attempt_id,
        enums::WebhookDeliveryAttempt::InitialAttempt
        | enums::WebhookDeliveryAttempt::AutomaticRetry,
    )) = initial_attempt_id.zip(delivery_attempt)
    {
        state
            .store
            .update_event_by_merchant_id_event_id(
                key_manager_state,
                merchant_id,
                initial_attempt_id.as_str(),
                update_overall_delivery_status,
                &merchant_key_store,
            )
            .await
            .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed)
            .attach_printable("Failed to update initial delivery attempt")?;
    }

    Ok(())
}

async fn handle_successful_delivery(
    state: SessionState,
    merchant_key_store: domain::MerchantKeyStore,
    updated_event: &domain::Event,
    merchant_id: &common_utils::id_type::MerchantId,
    process_tracker: Option<storage::ProcessTracker>,
    business_status: &'static str,
) -> CustomResult<(), errors::WebhooksFlowError> {
    update_overall_delivery_status_in_storage(
        state.clone(),
        merchant_key_store.clone(),
        merchant_id,
        updated_event,
    )
    .await?;

    increment_webhook_outgoing_received_count(merchant_id);

    match process_tracker {
        Some(process_tracker) => state
            .store
            .as_scheduler()
            .finish_process_with_business_status(process_tracker, business_status)
            .await
            .change_context(
                errors::WebhooksFlowError::OutgoingWebhookProcessTrackerTaskUpdateFailed,
            ),
        None => Ok(()),
    }
}

async fn handle_failed_delivery(
    _state: SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
    delivery_attempt: enums::WebhookDeliveryAttempt,
    status_code: u16,
    log_message: &'static str,
    _schedule_webhook_retry: types::ScheduleWebhookRetry,
) -> CustomResult<(), errors::WebhooksFlowError> {
    utils::increment_webhook_outgoing_not_received_count(merchant_id);

    let error = report!(errors::WebhooksFlowError::NotReceivedByMerchant);
    logger::warn!(?error, ?delivery_attempt, status_code, %log_message);

    //TODO: add outgoing webhook retries support
    // if let ScheduleWebhookRetry::WithProcessTracker(process_tracker) = schedule_webhook_retry {
    //     // Schedule a retry attempt for webhook delivery
    //     outgoing_webhook_retry::retry_webhook_delivery_task(
    //         &*state.store,
    //         merchant_id,
    //         *process_tracker,
    //     )
    //     .await
    //     .change_context(errors::WebhooksFlowError::OutgoingWebhookRetrySchedulingFailed)?;
    // }

    Err(error)
}

impl ForeignFrom<&api::OutgoingWebhookContent> for storage::EventMetadata {
    fn foreign_from(content: &api::OutgoingWebhookContent) -> Self {
        match content {
            webhooks::OutgoingWebhookContent::PaymentDetails(payments_response) => Self::Payment {
                payment_id: payments_response.id.clone(),
            },
            webhooks::OutgoingWebhookContent::RefundDetails(refund_response) => Self::Refund {
                payment_id: refund_response.payment_id.clone(),
                refund_id: refund_response.id.clone(),
            },
            webhooks::OutgoingWebhookContent::DisputeDetails(dispute_response) => {
                //TODO: add support for dispute outgoing webhook
                todo!()
            }
            webhooks::OutgoingWebhookContent::MandateDetails(mandate_response) => Self::Mandate {
                payment_method_id: mandate_response.payment_method_id.clone(),
                mandate_id: mandate_response.mandate_id.clone(),
            },
            #[cfg(feature = "payouts")]
            webhooks::OutgoingWebhookContent::PayoutDetails(payout_response) => Self::Payout {
                payout_id: payout_response.payout_id.clone(),
            },
        }
    }
}

impl ForeignFrom<storage::EventMetadata> for outgoing_webhook_logs::OutgoingWebhookEventContent {
    fn foreign_from(event_metadata: storage::EventMetadata) -> Self {
        match event_metadata {
            diesel_models::EventMetadata::Payment { payment_id } => Self::Payment {
                payment_id,
                content: serde_json::Value::Null,
            },
            diesel_models::EventMetadata::Payout { payout_id } => Self::Payout {
                payout_id,
                content: serde_json::Value::Null,
            },
            diesel_models::EventMetadata::Refund {
                payment_id,
                refund_id,
            } => Self::Refund {
                payment_id,
                refund_id,
                content: serde_json::Value::Null,
            },
            diesel_models::EventMetadata::Dispute {
                payment_id,
                attempt_id,
                dispute_id,
            } => Self::Dispute {
                payment_id,
                attempt_id,
                dispute_id,
                content: serde_json::Value::Null,
            },
            diesel_models::EventMetadata::Mandate {
                payment_method_id,
                mandate_id,
            } => Self::Mandate {
                payment_method_id,
                mandate_id,
                content: serde_json::Value::Null,
            },
        }
    }
}

trait OutgoingWebhookResponseHandler {
    async fn handle_error_response(
        &self,
        state: SessionState,
        merchant_key_store: domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
        event_id: &str,
        client_error: Report<errors::ApiClientError>,
    ) -> CustomResult<
        (domain::Event, Option<Report<errors::WebhooksFlowError>>),
        errors::WebhooksFlowError,
    >;

    async fn handle_success_response(
        &self,
        state: SessionState,
        merchant_key_store: domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
        event_id: &str,
        process_tracker: Option<storage::ProcessTracker>,
        response: reqwest::Response,
    ) -> CustomResult<
        (domain::Event, Option<Report<errors::WebhooksFlowError>>),
        errors::WebhooksFlowError,
    >;
}

impl OutgoingWebhookResponseHandler for enums::WebhookDeliveryAttempt {
    async fn handle_error_response(
        &self,
        state: SessionState,
        merchant_key_store: domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
        event_id: &str,
        client_error: Report<errors::ApiClientError>,
    ) -> CustomResult<
        (domain::Event, Option<Report<errors::WebhooksFlowError>>),
        errors::WebhooksFlowError,
    > {
        let schedule_webhook_retry = match self {
            Self::InitialAttempt | Self::ManualRetry => types::ScheduleWebhookRetry::NoSchedule,
            Self::AutomaticRetry => {
                // ScheduleWebhookRetry::WithProcessTracker(Box::new(process_tracker))
                todo!()
            }
        };

        api_client_error_handler(
            state,
            merchant_key_store,
            merchant_id,
            event_id,
            client_error,
            *self,
            schedule_webhook_retry,
        )
        .await
    }

    async fn handle_success_response(
        &self,
        state: SessionState,
        merchant_key_store: domain::MerchantKeyStore,
        merchant_id: &common_utils::id_type::MerchantId,
        event_id: &str,
        process_tracker: Option<storage::ProcessTracker>,
        response: reqwest::Response,
    ) -> CustomResult<
        (domain::Event, Option<Report<errors::WebhooksFlowError>>),
        errors::WebhooksFlowError,
    > {
        let status_code = response.status();
        let is_webhook_notified = status_code.is_success();
        let response_struct = types::WebhookResponse { response };
        let outgoing_webhook_response = response_struct
            .get_outgoing_webhook_response_content()
            .await;
        let updated_event = update_event_in_storage(
            state.clone(),
            is_webhook_notified,
            outgoing_webhook_response,
            merchant_key_store.clone(),
            merchant_id,
            event_id,
        )
        .await?;

        let webhook_action_handler = get_action_handler(*self);
        let result = if is_webhook_notified {
            webhook_action_handler
                .notified_action(
                    state,
                    merchant_key_store,
                    &updated_event,
                    merchant_id,
                    process_tracker,
                )
                .await
        } else {
            webhook_action_handler
                .not_notified_action(state, merchant_id, status_code.as_u16())
                .await
        };

        Ok((updated_event, result))
    }
}

#[async_trait::async_trait]
trait WebhookNotificationHandler: Send + Sync {
    async fn notified_action(
        &self,
        state: SessionState,
        merchant_key_store: domain::MerchantKeyStore,
        updated_event: &domain::Event,
        merchant_id: &common_utils::id_type::MerchantId,
        process_tracker: Option<storage::ProcessTracker>,
    ) -> Option<Report<errors::WebhooksFlowError>>;

    async fn not_notified_action(
        &self,
        state: SessionState,
        merchant_id: &common_utils::id_type::MerchantId,
        status_code: u16,
    ) -> Option<Report<errors::WebhooksFlowError>>;
}

struct InitialAttempt;
struct AutomaticRetry;
struct ManualRetry;

#[async_trait::async_trait]
impl WebhookNotificationHandler for InitialAttempt {
    async fn notified_action(
        &self,
        state: SessionState,
        merchant_key_store: domain::MerchantKeyStore,
        updated_event: &domain::Event,
        merchant_id: &common_utils::id_type::MerchantId,
        process_tracker: Option<storage::ProcessTracker>,
    ) -> Option<Report<errors::WebhooksFlowError>> {
        handle_successful_delivery(
            state,
            merchant_key_store,
            updated_event,
            merchant_id,
            process_tracker,
            business_status::INITIAL_DELIVERY_ATTEMPT_SUCCESSFUL,
        )
        .await
        .err()
        .map(|error: Report<errors::WebhooksFlowError>| report!(error))
    }

    async fn not_notified_action(
        &self,
        state: SessionState,
        merchant_id: &common_utils::id_type::MerchantId,
        status_code: u16,
    ) -> Option<Report<errors::WebhooksFlowError>> {
        handle_failed_delivery(
            state.clone(),
            merchant_id,
            enums::WebhookDeliveryAttempt::InitialAttempt,
            status_code,
            "Ignoring error when sending webhook to merchant",
            types::ScheduleWebhookRetry::NoSchedule,
        )
        .await
        .err()
        .map(|error| report!(error))
    }
}

#[async_trait::async_trait]
impl WebhookNotificationHandler for AutomaticRetry {
    async fn notified_action(
        &self,
        _state: SessionState,
        _merchant_key_store: domain::MerchantKeyStore,
        _updated_event: &domain::Event,
        _merchant_id: &common_utils::id_type::MerchantId,
        _process_tracker: Option<storage::ProcessTracker>,
    ) -> Option<Report<errors::WebhooksFlowError>> {
        todo!()
    }

    async fn not_notified_action(
        &self,
        _state: SessionState,
        _merchant_id: &common_utils::id_type::MerchantId,
        _status_code: u16,
    ) -> Option<Report<errors::WebhooksFlowError>> {
        todo!()
    }
}

#[async_trait::async_trait]
impl WebhookNotificationHandler for ManualRetry {
    async fn notified_action(
        &self,
        _state: SessionState,
        _merchant_key_store: domain::MerchantKeyStore,
        _updated_event: &domain::Event,
        merchant_id: &common_utils::id_type::MerchantId,
        _process_tracker: Option<storage::ProcessTracker>,
    ) -> Option<Report<errors::WebhooksFlowError>> {
        increment_webhook_outgoing_received_count(merchant_id);
        None
    }

    async fn not_notified_action(
        &self,
        state: SessionState,
        merchant_id: &common_utils::id_type::MerchantId,
        status_code: u16,
    ) -> Option<Report<errors::WebhooksFlowError>> {
        handle_failed_delivery(
            state.clone(),
            merchant_id,
            enums::WebhookDeliveryAttempt::ManualRetry,
            status_code,
            "Ignoring error when sending webhook to merchant",
            types::ScheduleWebhookRetry::NoSchedule,
        )
        .await
        .err()
        .map(|error| report!(error))
    }
}

fn get_action_handler(
    attempt: enums::WebhookDeliveryAttempt,
) -> Box<dyn WebhookNotificationHandler> {
    match attempt {
        enums::WebhookDeliveryAttempt::InitialAttempt => Box::new(InitialAttempt),
        enums::WebhookDeliveryAttempt::AutomaticRetry => Box::new(AutomaticRetry),
        enums::WebhookDeliveryAttempt::ManualRetry => Box::new(ManualRetry),
    }
}


pub struct TrustpayApplePayResponse {
    pub country_code: api_models::enums::CountryAlpha2,
    pub currency_code: api_models::enums::Currency,
    pub supported_networks: Vec<String>,
    pub merchant_capabilities: Vec<String>,
    pub total: ApplePayTotalInfo,
}


pub async fn api_key_create() {}


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

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }
    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.signifyd.base_url.as_ref()
    }

    #[cfg(feature = "frm")]
    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let auth = signifyd::SignifydAuthType::try_from(auth_type)
            .change_context(ConnectorError::FailedToObtainAuthType)?;
        let auth_api_key = format!(
            "Basic {}",
            consts::BASE64_ENGINE.encode(auth.api_key.peek())
        );

        Ok(vec![(
            headers::AUTHORIZATION.to_string(),
            Mask::into_masked(auth_api_key),
        )])
    }

    #[cfg(feature = "frm")]
    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        let response: signifyd::SignifydErrorResponse = res
            .response
            .parse_struct("SignifydErrorResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: NO_ERROR_CODE.to_string(),
            message: response.messages.join(" &"),
            reason: Some(response.errors.to_string()),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


    connectors::Tsys,
    connectors::UnifiedAuthenticationService,
    connectors::Wise,
    connectors::Worldline,
    connectors::Worldpay,
    connectors::Worldpayvantiv,
    connectors::Worldpayxml,
    connectors::Wellsfargo,
    connectors::Wellsfargopayout,
    connectors::Volt,
    connectors::Xendit,
    connectors::Zen,
    connectors::Zsl,
    connectors::CtpMastercard
);

#[cfg(feature = "frm")]
macro_rules! default_imp_for_frm_fulfillment {
    ($($path:ident::$connector:ident),*) => {
        $(
            impl FraudCheckFulfillment for $path::$connector {}
            impl
            ConnectorIntegration<
            Fulfillment,
            FraudCheckFulfillmentData,
            FraudCheckResponseData,
        > for $path::$connector
        {}
    )*
    };
}

#[cfg(feature = "frm")]
default_imp_for_frm_fulfillment!(
    connectors::Vgs,
    connectors::Aci,
    connectors::Adyen,
    connectors::Adyenplatform,
    connectors::Affirm,
    connectors::Airwallex,
    connectors::Amazonpay,
    connectors::Archipel,
    connectors::Authipay,
    connectors::Authorizedotnet,
    connectors::Bambora,
    connectors::Bamboraapac,
    connectors::Bankofamerica,
    connectors::Barclaycard,
    connectors::Billwerk,
    connectors::Bitpay,
    connectors::Blackhawknetwork,
    connectors::Bluecode,
    connectors::Bluesnap,
    connectors::Braintree,
    connectors::Boku,
    connectors::Breadpay,
    connectors::Cashtocode,
    connectors::Celero,
    connectors::Chargebee,
    connectors::Checkbook,
    connectors::Checkout,
    connectors::Coinbase,
    connectors::Coingate,
    connectors::Cryptopay,
    connectors::Custombilling,
    connectors::Cybersource,
    connectors::Datatrans,
    connectors::Deutschebank,
    connectors::Digitalvirgo,
    connectors::Dlocal,
    connectors::Dwolla,
    connectors::Ebanx,
    connectors::Elavon,
    connectors::Facilitapay,
    connectors::Fiserv,
    connectors::Fiservemea,
    connectors::Fiuu,
    connectors::Flexiti,
    connectors::Forte,
    connectors::Getnet,
    connectors::Globalpay,
    connectors::Globepay,
    connectors::Gocardless,
    connectors::Gpayments,
    connectors::Hipay,
    connectors::Helcim,
    connectors::HyperswitchVault,
    connectors::Hyperwallet,
    connectors::Iatapay,
    connectors::Inespay,
    connectors::Itaubank,
    connectors::Jpmorgan,
    connectors::Juspaythreedsserver,
    connectors::Katapult,
    connectors::Klarna,
    connectors::Netcetera,
    connectors::Nmi,
    connectors::Nomupay,
    connectors::Noon,
    connectors::Nordea,
    connectors::Novalnet,
    connectors::Nexinets,
    connectors::Nexixpay,
    connectors::Opayo,
    connectors::Opennode,
    connectors::Nuvei,
    connectors::Payone,
    connectors::Paybox,
    connectors::Payeezy,
    connectors::Payload,
    connectors::Payme,
    connectors::Paypal,
    connectors::Paysafe,
    connectors::Paystack,
    connectors::Paytm,
    connectors::Payu,
    connectors::Phonepe,
    connectors::Placetopay,
    connectors::Plaid,
    connectors::Powertranz,
    connectors::Prophetpay,
    connectors::Mifinity,
    connectors::Mollie,
    connectors::Moneris,
    connectors::Mpgs,
    connectors::Multisafepay,
    connectors::Rapyd,
    connectors::Razorpay,
    connectors::Recurly,
    connectors::Redsys,
    connectors::Santander,
    connectors::Shift4,
    connectors::Sift,
    connectors::Silverflow,
    connectors::Stax,
    connectors::Square,
    connectors::Stripe,
    connectors::Stripebilling,
    connectors::Taxjar,
    connectors::Threedsecureio,
    connectors::Thunes,
    connectors::Tokenio,
    connectors::Trustpay,
    connectors::Trustpayments,
    connectors::Tsys,
    connectors::UnifiedAuthenticationService,
    connectors::Wise,
    connectors::Worldline,
    connectors::Worldpay,
    connectors::Worldpayvantiv,
    connectors::Worldpayxml,
    connectors::Wellsfargo,
    connectors::Wellsfargopayout,
    connectors::Volt,
    connectors::Xendit,
    connectors::Zen,
    connectors::Zsl,
    connectors::CtpMastercard
);

#[cfg(feature = "frm")]
macro_rules! default_imp_for_frm_record_return {
    ($($path:ident::$connector:ident),*) => {
        $(
            impl FraudCheckRecordReturn for $path::$connector {}
            impl
            ConnectorIntegration<
            RecordReturn,
            FraudCheckRecordReturnData,
            FraudCheckResponseData,
        > for $path::$connector
        {}
    )*
    };
}

#[cfg(feature = "frm")]
default_imp_for_frm_record_return!(
    connectors::Vgs,
    connectors::Aci,
    connectors::Adyen,
    connectors::Adyenplatform,
    connectors::Affirm,
    connectors::Airwallex,
    connectors::Amazonpay,
    connectors::Archipel,
    connectors::Authipay,
    connectors::Authorizedotnet,
    connectors::Bambora,
    connectors::Bamboraapac,
    connectors::Bankofamerica,
    connectors::Barclaycard,
    connectors::Billwerk,
    connectors::Bitpay,
    connectors::Blackhawknetwork,
    connectors::Bluecode,
    connectors::Bluesnap,
    connectors::Braintree,
    connectors::Boku,
    connectors::Breadpay,
    connectors::Cashtocode,
    connectors::Celero,
    connectors::Chargebee,
    connectors::Checkbook,
    connectors::Checkout,
    connectors::Coinbase,
    connectors::Coingate,
    connectors::Cryptopay,
    connectors::Custombilling,
    connectors::Cybersource,
    connectors::Datatrans,
    connectors::Deutschebank,
    connectors::Digitalvirgo,
    connectors::Dlocal,
    connectors::Dwolla,
    connectors::Ebanx,
    connectors::Elavon,
    connectors::Facilitapay,
    connectors::Fiserv,
    connectors::Fiservemea,
    connectors::Fiuu,
    connectors::Flexiti,
    connectors::Forte,
    connectors::Getnet,
    connectors::Globalpay,
    connectors::Globepay,
    connectors::Gocardless,
    connectors::Gpayments,
    connectors::Hipay,
    connectors::Helcim,
    connectors::HyperswitchVault,
    connectors::Hyperwallet,
    connectors::Iatapay,
    connectors::Inespay,
    connectors::Itaubank,
    connectors::Jpmorgan,
    connectors::Juspaythreedsserver,
    connectors::Katapult,
    connectors::Klarna,
    connectors::Netcetera,
    connectors::Nmi,
    connectors::Nomupay,
    connectors::Noon,
    connectors::Nordea,
    connectors::Novalnet,
    connectors::Nexinets,
    connectors::Nexixpay,
    connectors::Opayo,
    connectors::Opennode,
    connectors::Nuvei,
    connectors::Paybox,
    connectors::Payeezy,
    connectors::Payload,
    connectors::Payme,
    connectors::Payone,
    connectors::Paypal,
    connectors::Paysafe,
    connectors::Paystack,
    connectors::Paytm,
    connectors::Payu,
    connectors::Phonepe,
    connectors::Placetopay,
    connectors::Plaid,
    connectors::Powertranz,
    connectors::Prophetpay,
    connectors::Mifinity,
    connectors::Mollie,
    connectors::Moneris,
    connectors::Mpgs,
    connectors::Multisafepay,
    connectors::Rapyd,
    connectors::Razorpay,
    connectors::Recurly,
    connectors::Redsys,
    connectors::Santander,
    connectors::Shift4,
    connectors::Sift,
    connectors::Silverflow,
    connectors::Stripe,
    connectors::Stax,
    connectors::Square,
    connectors::Stripebilling,
    connectors::Taxjar,
    connectors::Threedsecureio,
    connectors::Thunes,
    connectors::Tokenio,
    connectors::Trustpay,
    connectors::Trustpayments,
    connectors::Tsys,
    connectors::UnifiedAuthenticationService,
    connectors::Wise,
    connectors::Worldline,
    connectors::Worldpay,
    connectors::Worldpayvantiv,
    connectors::Worldpayxml,
    connectors::Wellsfargo,
    connectors::Wellsfargopayout,
    connectors::Volt,
    connectors::Xendit,
    connectors::Zen,
    connectors::Zsl,
    connectors::CtpMastercard
);

macro_rules! default_imp_for_revoking_mandates {
    ($($path:ident::$connector:ident),*) => {
        $( impl ConnectorMandateRevoke for $path::$connector {}
            impl
            ConnectorIntegration<
            MandateRevoke,
            MandateRevokeRequestData,
            MandateRevokeResponseData,
        > for $path::$connector
        {}
    )*
    };
}

default_imp_for_revoking_mandates!(
    connectors::Vgs,
    connectors::Aci,
    connectors::Adyen,
    connectors::Adyenplatform,
    connectors::Affirm,
    connectors::Airwallex,
    connectors::Amazonpay,
    connectors::Archipel,
    connectors::Authipay,
    connectors::Authorizedotnet,
    connectors::Bambora,
    connectors::Bamboraapac,
    connectors::Bankofamerica,
    connectors::Barclaycard,
    connectors::Billwerk,
    connectors::Bitpay,
    connectors::Blackhawknetwork,
    connectors::Bluecode,
    connectors::Bluesnap,
    connectors::Boku,
    connectors::Breadpay,
    connectors::Cashtocode,
    connectors::Celero,
    connectors::Chargebee,
    connectors::Checkbook,
    connectors::Checkout,
    connectors::Coinbase,
    connectors::Coingate,
    connectors::Cryptopay,
    connectors::Custombilling,
    connectors::Datatrans,
    connectors::Deutschebank,
    connectors::Digitalvirgo,
    connectors::Dlocal,
    connectors::Dwolla,
    connectors::Ebanx,
    connectors::Elavon,
    connectors::Facilitapay,
    connectors::Fiserv,
    connectors::Fiservemea,
    connectors::Fiuu,
    connectors::Flexiti,
    connectors::Forte,
    connectors::Getnet,
    connectors::Globalpay,
    connectors::Globepay,
    connectors::Gocardless,
    connectors::Gpayments,
    connectors::Hipay,
    connectors::Helcim,
    connectors::HyperswitchVault,
    connectors::Hyperwallet,
    connectors::Iatapay,
    connectors::Inespay,
    connectors::Itaubank,
    connectors::Jpmorgan,
    connectors::Juspaythreedsserver,
    connectors::Katapult,
    connectors::Klarna,
    connectors::Netcetera,
    connectors::Nmi,
    connectors::Nomupay,
    connectors::Nordea,
    connectors::Novalnet,
    connectors::Nexinets,
    connectors::Nexixpay,
    connectors::Opayo,
    connectors::Opennode,
    connectors::Nuvei,
    connectors::Paybox,
    connectors::Payeezy,
    connectors::Payload,
    connectors::Payme,
    connectors::Payone,
    connectors::Paypal,
    connectors::Paysafe,
    connectors::Paystack,
    connectors::Paytm,
    connectors::Payu,
    connectors::Phonepe,
    connectors::Placetopay,
    connectors::Plaid,
    connectors::Powertranz,
    connectors::Prophetpay,
    connectors::Mifinity,
    connectors::Mollie,
    connectors::Moneris,
    connectors::Mpgs,
    connectors::Multisafepay,
    connectors::Rapyd,
    connectors::Razorpay,
    connectors::Recurly,
    connectors::Redsys,
    connectors::Riskified,
    connectors::Santander,
    connectors::Shift4,
    connectors::Sift,
    connectors::Silverflow,
    connectors::Signifyd,
    connectors::Stax,
    connectors::Square,
    connectors::Stripe,
    connectors::Stripebilling,
    connectors::Taxjar,
    connectors::Threedsecureio,
    connectors::Thunes,
    connectors::Tokenio,
    connectors::Trustpay,
    connectors::Trustpayments,
    connectors::Tsys,
    connectors::UnifiedAuthenticationService,
    connectors::Wise,
    connectors::Worldline,
    connectors::Worldpay,
    connectors::Worldpayvantiv,
    connectors::Worldpayxml,
    connectors::Volt,
    connectors::Xendit,
    connectors::Zen,
    connectors::Zsl,
    connectors::CtpMastercard
);

macro_rules! default_imp_for_uas_pre_authentication {
    ($($path:ident::$connector:ident),*) => {
        $( impl UnifiedAuthenticationService for $path::$connector {}
            impl UasPreAuthentication for $path::$connector {}
            impl
            ConnectorIntegration<
            PreAuthenticate,
            UasPreAuthenticationRequestData,
            UasAuthenticationResponseData
        > for $path::$connector
        {}
    )*
    };
}

default_imp_for_uas_pre_authentication!(
    connectors::Vgs,
    connectors::Aci,
    connectors::Adyen,
    connectors::Adyenplatform,
    connectors::Affirm,
    connectors::Airwallex,
    connectors::Amazonpay,
    connectors::Archipel,
    connectors::Authipay,
    connectors::Authorizedotnet,
    connectors::Bambora,
    connectors::Bamboraapac,
    connectors::Bankofamerica,
    connectors::Barclaycard,
    connectors::Billwerk,
    connectors::Bluesnap,
    connectors::Bitpay,
    connectors::Blackhawknetwork,
    connectors::Bluecode,
    connectors::Braintree,
    connectors::Boku,
    connectors::Breadpay,
    connectors::Cashtocode,
    connectors::Celero,
    connectors::Chargebee,
    connectors::Checkbook,
    connectors::Checkout,
    connectors::Coinbase,
    connectors::Coingate,
    connectors::Cryptopay,
    connectors::CtpMastercard,
    connectors::Custombilling,
    connectors::Cybersource,
    connectors::Datatrans,
    connectors::Deutschebank,
    connectors::Digitalvirgo,
    connectors::Dlocal,
    connectors::Dwolla,
    connectors::Ebanx,
    connectors::Elavon,
    connectors::Facilitapay,
    connectors::Fiserv,
    connectors::Fiservemea,
    connectors::Fiuu,
    connectors::Flexiti,
    connectors::Forte,
    connectors::Getnet,
    connectors::Globalpay,
    connectors::Globepay,
    connectors::Gocardless,
    connectors::Gpayments,
    connectors::Hipay,
    connectors::Helcim,
    connectors::HyperswitchVault,
    connectors::Hyperwallet,
    connectors::Iatapay,
    connectors::Inespay,
    connectors::Itaubank,
    connectors::Jpmorgan,
    connectors::Katapult,
    connectors::Klarna,
    connectors::Netcetera,
    connectors::Nmi,
    connectors::Nomupay,
    connectors::Noon,
    connectors::Nordea,
    connectors::Novalnet,
    connectors::Nexinets,
    connectors::Nexixpay,
    connectors::Opayo,
    connectors::Opennode,
    connectors::Nuvei,
    connectors::Payeezy,
    connectors::Payload,
    connectors::Payme,
    connectors::Payone,
    connectors::Paypal,
    connectors::Paysafe,
    connectors::Paystack,
    connectors::Paytm,
    connectors::Payu,
    connectors::Phonepe,
    connectors::Powertranz,
    connectors::Prophetpay,
    connectors::Mifinity,
    connectors::Mollie,
    connectors::Moneris,
    connectors::Mpgs,
    connectors::Multisafepay,
    connectors::Paybox,
    connectors::Placetopay,
    connectors::Plaid,
    connectors::Rapyd,
    connectors::Razorpay,
    connectors::Recurly,
    connectors::Redsys,
    connectors::Riskified,
    connectors::Santander,
    connectors::Shift4,
    connectors::Sift,
    connectors::Silverflow,
    connectors::Signifyd,
    connectors::Stripe,
    connectors::Stax,
    connectors::Square,
    connectors::Stripebilling,
    connectors::Taxjar,
    connectors::Threedsecureio,
    connectors::Thunes,
    connectors::Tokenio,
    connectors::Trustpay,
    connectors::Trustpayments,
    connectors::Tsys,
    connectors::Wise,
    connectors::Worldline,
    connectors::Worldpay,
    connectors::Worldpayvantiv,
    connectors::Worldpayxml,
    connectors::Wellsfargo,
    connectors::Wellsfargopayout,
    connectors::Volt,
    connectors::Xendit,
    connectors::Zen,
    connectors::Zsl
);

macro_rules! default_imp_for_uas_post_authentication {
    ($($path:ident::$connector:ident),*) => {
        $( impl UasPostAuthentication for $path::$connector {}
            impl
            ConnectorIntegration<
                PostAuthenticate,
                UasPostAuthenticationRequestData,
                UasAuthenticationResponseData
        > for $path::$connector
        {}
    )*
    };
}

default_imp_for_uas_post_authentication!(
    connectors::Vgs,
    connectors::Aci,
    connectors::Adyen,
    connectors::Adyenplatform,
    connectors::Affirm,
    connectors::Airwallex,
    connectors::Amazonpay,
    connectors::Archipel,
    connectors::Authipay,
    connectors::Authorizedotnet,
    connectors::Bambora,
    connectors::Bamboraapac,
    connectors::Bankofamerica,
    connectors::Barclaycard,
    connectors::Billwerk,
    connectors::Bitpay,
    connectors::Blackhawknetwork,
    connectors::Bluecode,
    connectors::Bluesnap,
    connectors::Braintree,
    connectors::Boku,
    connectors::Breadpay,
    connectors::Cashtocode,
    connectors::Celero,
    connectors::Chargebee,
    connectors::Checkbook,
    connectors::Checkout,
    connectors::Coinbase,
    connectors::Coingate,
    connectors::Cryptopay,
    connectors::CtpMastercard,
    connectors::Custombilling,
    connectors::Cybersource,
    connectors::Datatrans,
    connectors::Deutschebank,
    connectors::Digitalvirgo,
    connectors::Dlocal,
    connectors::Dwolla,
    connectors::Ebanx,
    connectors::Elavon,
    connectors::Facilitapay,
    connectors::Fiserv,
    connectors::Fiservemea,
    connectors::Fiuu,
    connectors::Flexiti,
    connectors::Forte,
    connectors::Getnet,
    connectors::Globalpay,
    connectors::Globepay,
    connectors::Gocardless,
    connectors::Gpayments,
    connectors::Hipay,
    connectors::Helcim,
    connectors::HyperswitchVault,
    connectors::Hyperwallet,
    connectors::Iatapay,
    connectors::Inespay,
    connectors::Itaubank,
    connectors::Jpmorgan,
    connectors::Katapult,
    connectors::Klarna,
    connectors::Netcetera,
    connectors::Nmi,
    connectors::Nomupay,
    connectors::Noon,
    connectors::Nordea,
    connectors::Novalnet,
    connectors::Nexinets,
    connectors::Nexixpay,
    connectors::Opayo,
    connectors::Opennode,
    connectors::Nuvei,
    connectors::Payeezy,
    connectors::Payload,
    connectors::Payme,
    connectors::Payone,
    connectors::Paypal,
    connectors::Paysafe,
    connectors::Paystack,
    connectors::Paytm,
    connectors::Payu,
    connectors::Phonepe,
    connectors::Powertranz,
    connectors::Prophetpay,
    connectors::Plaid,
    connectors::Mifinity,
    connectors::Mollie,
    connectors::Moneris,
    connectors::Mpgs,
    connectors::Multisafepay,
    connectors::Paybox,
    connectors::Placetopay,
    connectors::Rapyd,
    connectors::Razorpay,
    connectors::Recurly,
    connectors::Redsys,
    connectors::Riskified,
    connectors::Santander,
    connectors::Shift4,
    connectors::Sift,
    connectors::Silverflow,
    connectors::Signifyd,
    connectors::Stripe,
    connectors::Stax,
    connectors::Square,
    connectors::Stripebilling,
    connectors::Taxjar,
    connectors::Threedsecureio,
    connectors::Thunes,
    connectors::Tokenio,
    connectors::Trustpay,
    connectors::Trustpayments,
    connectors::Tsys,
    connectors::Wise,
    connectors::Worldline,
    connectors::Worldpay,
    connectors::Worldpayvantiv,
    connectors::Worldpayxml,
    connectors::Wellsfargo,
    connectors::Wellsfargopayout,
    connectors::Volt,
    connectors::Xendit,
    connectors::Zen,
    connectors::Zsl
);

macro_rules! default_imp_for_uas_authentication_confirmation {
    ($($path:ident::$connector:ident),*) => {
        $( impl UasAuthenticationConfirmation for $path::$connector {}
            impl
            ConnectorIntegration<
            AuthenticationConfirmation,
            UasConfirmationRequestData,
            UasAuthenticationResponseData

        > for $path::$connector
        {}
        )*
    };
}

default_imp_for_uas_authentication_confirmation!(
    connectors::Vgs,
    connectors::Aci,
    connectors::Adyen,
    connectors::Adyenplatform,
    connectors::Affirm,
    connectors::Airwallex,
    connectors::Amazonpay,
    connectors::Archipel,
    connectors::Authipay,
    connectors::Authorizedotnet,
    connectors::Bambora,
    connectors::Bamboraapac,
    connectors::Bankofamerica,
    connectors::Barclaycard,
    connectors::Billwerk,
    connectors::Bluesnap,
    connectors::Bitpay,
    connectors::Blackhawknetwork,
    connectors::Bluecode,
    connectors::Braintree,
    connectors::Boku,
    connectors::Breadpay,
    connectors::Cashtocode,
    connectors::Celero,
    connectors::Chargebee,
    connectors::Checkbook,
    connectors::Checkout,
    connectors::Coinbase,
    connectors::Coingate,
    connectors::Cryptopay,
    connectors::CtpMastercard,
    connectors::Custombilling,
    connectors::Cybersource,
    connectors::Datatrans,
    connectors::Deutschebank,
    connectors::Digitalvirgo,
    connectors::Dlocal,
    connectors::Dwolla,
    connectors::Ebanx,
    connectors::Elavon,
    connectors::Facilitapay,
    connectors::Fiserv,
    connectors::Fiservemea,
    connectors::Fiuu,
    connectors::Flexiti,
    connectors::Forte,
    connectors::Getnet,
    connectors::Globalpay,
    connectors::Globepay,
    connectors::Gocardless,
    connectors::Gpayments,
    connectors::Hipay,
    connectors::Helcim,
    connectors::HyperswitchVault,
    connectors::Hyperwallet,
    connectors::Iatapay,
    connectors::Inespay,
    connectors::Itaubank,
    connectors::Jpmorgan,
    connectors::Katapult,
    connectors::Klarna,
    connectors::Netcetera,
    connectors::Nmi,
    connectors::Nomupay,
    connectors::Noon,
    connectors::Nordea,
    connectors::Novalnet,
    connectors::Nexinets,
    connectors::Nexixpay,
    connectors::Opayo,
    connectors::Opennode,
    connectors::Nuvei,
    connectors::Payeezy,
    connectors::Payload,
    connectors::Payme,
    connectors::Payone,
    connectors::Paystack,
    connectors::Paytm,
    connectors::Payu,
    connectors::Phonepe,
    connectors::Powertranz,
    connectors::Prophetpay,
    connectors::Mifinity,
    connectors::Mollie,
    connectors::Moneris,
    connectors::Mpgs,
    connectors::Multisafepay,
    connectors::Paybox,
    connectors::Paypal,
    connectors::Paysafe,
    connectors::Placetopay,
    connectors::Plaid,
    connectors::Rapyd,
    connectors::Razorpay,
    connectors::Recurly,
    connectors::Redsys,
    connectors::Riskified,
    connectors::Santander,
    connectors::Shift4,
    connectors::Sift,
    connectors::Silverflow,
    connectors::Signifyd,
    connectors::Stax,
    connectors::Square,
    connectors::Stripe,
    connectors::Stripebilling,
    connectors::Taxjar,
    connectors::Threedsecureio,
    connectors::Thunes,
    connectors::Tokenio,
    connectors::Trustpay,
    connectors::Trustpayments,
    connectors::Tsys,
    connectors::Wise,
    connectors::Worldline,
    connectors::Worldpay,
    connectors::Worldpayvantiv,
    connectors::Worldpayxml,
    connectors::Wellsfargo,
    connectors::Wellsfargopayout,
    connectors::Volt,
    connectors::Xendit,
    connectors::Zen,
    connectors::Zsl
);

macro_rules! default_imp_for_connector_request_id {
    ($($path:ident::$connector:ident),*) => {
        $(
            impl ConnectorTransactionId for $path::$connector {}
    )*
    };
}
default_imp_for_connector_request_id!(
    connectors::Vgs,
    connectors::Aci,
    connectors::Adyen,
    connectors::Adyenplatform,
    connectors::Affirm,
    connectors::Airwallex,
    connectors::Archipel,
    connectors::Authipay,
    connectors::Authorizedotnet,
    connectors::Amazonpay,
    connectors::Bambora,
    connectors::Bamboraapac,
    connectors::Bankofamerica,
    connectors::Barclaycard,
    connectors::Billwerk,
    connectors::Bluesnap,
    connectors::Bitpay,
    connectors::Blackhawknetwork,
    connectors::Bluecode,
    connectors::Braintree,
    connectors::Boku,
    connectors::Breadpay,
    connectors::Cashtocode,
    connectors::Celero,
    connectors::Chargebee,
    connectors::Checkbook,
    connectors::Checkout,
    connectors::Coinbase,
    connectors::Coingate,
    connectors::Cryptopay,
    connectors::CtpMastercard,
    connectors::Custombilling,
    connectors::Cybersource,
    connectors::Datatrans,
    connectors::Deutschebank,
    connectors::Digitalvirgo,
    connectors::Dlocal,
    connectors::Dwolla,
    connectors::Ebanx,
    connectors::Elavon,
    connectors::Facilitapay,
    connectors::Fiserv,
    connectors::Fiservemea,
    connectors::Fiuu,
    connectors::Flexiti,
    connectors::Forte,
    connectors::Getnet,
    connectors::Globalpay,
    connectors::Globepay,
    connectors::Gocardless,
    connectors::Gpayments,
    connectors::Hipay,
    connectors::HyperswitchVault,
    connectors::Hyperwallet,
    connectors::Iatapay,
    connectors::Inespay,
    connectors::Itaubank,
    connectors::Jpmorgan,
    connectors::Juspaythreedsserver,
    connectors::Katapult,
    connectors::Klarna,
    connectors::Netcetera,
    connectors::Nmi,
    connectors::Nomupay,
    connectors::Nordea,
    connectors::Novalnet,
    connectors::Noon,
    connectors::Nexixpay,
    connectors::Nuvei,
    connectors::Opayo,
    connectors::Opennode,
    connectors::Payeezy,
    connectors::Payload,
    connectors::Paystack,
    connectors::Paytm,
    connectors::Payu,
    connectors::Phonepe,
    connectors::Payme,
    connectors::Payone,
    connectors::Paypal,
    connectors::Paysafe,
    connectors::Powertranz,
    connectors::Prophetpay,
    connectors::Mifinity,
    connectors::Mollie,
    connectors::Moneris,
    connectors::Mpgs,
    connectors::Multisafepay,
    connectors::Paybox,
    connectors::Placetopay,
    connectors::Plaid,
    connectors::Rapyd,
    connectors::Razorpay,
    connectors::Recurly,
    connectors::Redsys,
    connectors::Riskified,
    connectors::Santander,
    connectors::Shift4,
    connectors::Sift,
    connectors::Silverflow,
    connectors::Signifyd,
    connectors::Stax,
    connectors::Square,
    connectors::Stripe,
    connectors::Stripebilling,
    connectors::Taxjar,
    connectors::Threedsecureio,
    connectors::Thunes,
    connectors::Tokenio,
    connectors::Trustpay,
    connectors::Trustpayments,
    connectors::Tsys,
    connectors::UnifiedAuthenticationService,
    connectors::Wise,
    connectors::Worldline,
    connectors::Worldpay,
    connectors::Worldpayvantiv,
    connectors::Worldpayxml,
    connectors::Wellsfargo,
    connectors::Wellsfargopayout,
    connectors::Volt,
    connectors::Xendit,
    connectors::Zen,
    connectors::Zsl
);

#[cfg(feature = "frm")]
macro_rules! default_imp_for_fraud_check {
    ($($path:ident::$connector:ident),*) => {
        $(
            impl FraudCheck for $path::$connector {}
    )*
    };
}
#[cfg(feature = "frm")]
default_imp_for_fraud_check!(
    connectors::Vgs,
    connectors::Aci,
    connectors::Adyen,
    connectors::Adyenplatform,
    connectors::Affirm,
    connectors::Airwallex,
    connectors::Amazonpay,
    connectors::Archipel,
    connectors::Authipay,
    connectors::Authorizedotnet,
    connectors::Bambora,
    connectors::Bamboraapac,
    connectors::Bankofamerica,
    connectors::Barclaycard,
    connectors::Billwerk,
    connectors::Bluesnap,
    connectors::Bitpay,
    connectors::Blackhawknetwork,
    connectors::Bluecode,
    connectors::Braintree,
    connectors::Boku,
    connectors::Breadpay,
    connectors::Cashtocode,
    connectors::Celero,
    connectors::Chargebee,
    connectors::Checkbook,
    connectors::Checkout,
    connectors::Coinbase,
    connectors::Coingate,
    connectors::Cryptopay,
    connectors::CtpMastercard,
    connectors::Custombilling,
    connectors::Cybersource,
    connectors::Datatrans,
    connectors::Deutschebank,
    connectors::Digitalvirgo,
    connectors::Dlocal,
    connectors::Dwolla,
    connectors::Ebanx,
    connectors::Elavon,
    connectors::Facilitapay,
    connectors::Fiserv,
    connectors::Fiservemea,
    connectors::Fiuu,
    connectors::Flexiti,
    connectors::Forte,
    connectors::Getnet,
    connectors::Globalpay,
    connectors::Globepay,
    connectors::Gocardless,
    connectors::Gpayments,
    connectors::Helcim,
    connectors::Hipay,
    connectors::HyperswitchVault,
    connectors::Hyperwallet,
    connectors::Iatapay,
    connectors::Inespay,
    connectors::Itaubank,
    connectors::Jpmorgan,
    connectors::Juspaythreedsserver,
    connectors::Katapult,
    connectors::Klarna,
    connectors::Netcetera,
    connectors::Nmi,
    connectors::Nomupay,
    connectors::Nordea,
    connectors::Novalnet,
    connectors::Noon,
    connectors::Nexinets,
    connectors::Nexixpay,
    connectors::Nuvei,
    connectors::Opayo,
    connectors::Payeezy,
    connectors::Payload,
    connectors::Paystack,
    connectors::Paytm,
    connectors::Paypal,
    connectors::Paysafe,
    connectors::Payu,
    connectors::Phonepe,
    connectors::Powertranz,
    connectors::Prophetpay,
    connectors::Mifinity,
    connectors::Mollie,
    connectors::Moneris,
    connectors::Mpgs,
    connectors::Multisafepay,
    connectors::Opennode,
    connectors::Paybox,
    connectors::Payme,
    connectors::Payone,
    connectors::Placetopay,
    connectors::Plaid,
    connectors::Rapyd,
    connectors::Razorpay,
    connectors::Recurly,
    connectors::Redsys,
    connectors::Santander,
    connectors::Shift4,
    connectors::Sift,
    connectors::Silverflow,
    connectors::Stax,
    connectors::Square,
    connectors::Stripe,
    connectors::Stripebilling,
    connectors::Taxjar,
    connectors::Threedsecureio,
    connectors::Thunes,
    connectors::Tokenio,
    connectors::Trustpay,
    connectors::Trustpayments,
    connectors::Tsys,
    connectors::UnifiedAuthenticationService,
    connectors::Wise,
    connectors::Worldline,
    connectors::Worldpay,
    connectors::Worldpayvantiv,
    connectors::Worldpayxml,
    connectors::Wellsfargo,
    connectors::Wellsfargopayout,
    connectors::Volt,
    connectors::Xendit,
    connectors::Zen,
    connectors::Zsl
);

macro_rules! default_imp_for_connector_authentication {
    ($($path:ident::$connector:ident),*) => {
        $( impl ExternalAuthentication for $path::$connector {}
            impl ConnectorAuthentication for $path::$connector {}
            impl ConnectorPreAuthentication for $path::$connector {}
            impl ConnectorPreAuthenticationVersionCall for $path::$connector {}
            impl ConnectorPostAuthentication for $path::$connector {}
            impl
            ConnectorIntegration<
            Authentication,
            authentication::ConnectorAuthenticationRequestData,
            AuthenticationResponseData,
        > for $path::$connector
        {}
        impl
            ConnectorIntegration<
            PreAuthentication,
            authentication::PreAuthNRequestData,
            AuthenticationResponseData,
        > for $path::$connector
        {}
        impl
            ConnectorIntegration<
            PreAuthenticationVersionCall,
            authentication::PreAuthNRequestData,
            AuthenticationResponseData,
        > for $path::$connector
        {}
        impl
            ConnectorIntegration<
            PostAuthentication,
            authentication::ConnectorPostAuthenticationRequestData,
            AuthenticationResponseData,
        > for $path::$connector
        {}
    )*
    };
}

default_imp_for_connector_authentication!(
    connectors::Vgs,
    connectors::Aci,
    connectors::Adyen,
    connectors::Adyenplatform,
    connectors::Affirm,
    connectors::Airwallex,
    connectors::Amazonpay,
    connectors::Archipel,
    connectors::Authipay,
    connectors::Authorizedotnet,
    connectors::Bambora,
    connectors::Bamboraapac,
    connectors::Bankofamerica,
    connectors::Barclaycard,
    connectors::Billwerk,
    connectors::Bluesnap,
    connectors::Bitpay,
    connectors::Blackhawknetwork,
    connectors::Bluecode,
    connectors::Braintree,
    connectors::Boku,
    connectors::Breadpay,
    connectors::Cashtocode,
    connectors::Celero,
    connectors::Chargebee,
    connectors::Checkbook,
    connectors::Checkout,
    connectors::Coinbase,
    connectors::Coingate,
    connectors::Cryptopay,
    connectors::CtpMastercard,
    connectors::Custombilling,
    connectors::Cybersource,
    connectors::Datatrans,
    connectors::Deutschebank,
    connectors::Digitalvirgo,
    connectors::Dlocal,
    connectors::Dwolla,
    connectors::Ebanx,
    connectors::Elavon,
    connectors::Facilitapay,
    connectors::Fiserv,
    connectors::Fiservemea,
    connectors::Fiuu,
    connectors::Flexiti,
    connectors::Forte,
    connectors::Getnet,
    connectors::Globalpay,
    connectors::Globepay,
    connectors::Gocardless,
    connectors::Hipay,
    connectors::Helcim,
    connectors::HyperswitchVault,
    connectors::Hyperwallet,
    connectors::Iatapay,
    connectors::Inespay,
    connectors::Itaubank,
    connectors::Jpmorgan,
    connectors::Juspaythreedsserver,
    connectors::Katapult,
    connectors::Klarna,
    connectors::Nmi,
    connectors::Nomupay,
    connectors::Noon,
    connectors::Nordea,
    connectors::Novalnet,
    connectors::Nexinets,
    connectors::Nexixpay,
    connectors::Nuvei,
    connectors::Opayo,
    connectors::Opennode,
    connectors::Payeezy,
    connectors::Payload,
    connectors::Paystack,
    connectors::Paytm,
    connectors::Payu,
    connectors::Phonepe,
    connectors::Payone,
    connectors::Powertranz,
    connectors::Prophetpay,
    connectors::Mifinity,
    connectors::Mollie,


pub struct ResultsResponseData {
    /// Universally unique transaction identifier assigned by the 3DS Server to identify a single transaction.
    /// It has the same value as the authentication request and conforms to the format defined in IETF RFC 4122.
    #[serde(rename = "threeDSServerTransID")]
    pub three_ds_server_trans_id: String,

    /// Indicates the status of a transaction in terms of its authentication.
    ///
    /// Valid values:
    /// - `Y`: Authentication / Account verification successful.
    /// - `N`: Not authenticated / Account not verified; Transaction denied.
    /// - `U`: Authentication / Account verification could not be performed; technical or other problem.
    /// - `C`: A challenge is required to complete the authentication.
    /// - `R`: Authentication / Account verification Rejected. Issuer is rejecting authentication/verification
    ///   and request that authorization not be attempted.
    /// - `A`: Attempts processing performed; Not authenticated / verified, but a proof of attempt
    ///   authentication / verification is provided.
    /// - `D`: A challenge is required to complete the authentication. Decoupled Authentication confirmed.
    /// - `I`: Informational Only; 3DS Requestor challenge preference acknowledged.
    pub trans_status: Option<common_enums::TransactionStatus>,

    /// Payment System-specific value provided as part of the ACS registration for each supported DS.
    /// Authentication Value may be used to provide proof of authentication.
    pub authentication_value: Option<Secret<String>>,

    /// Payment System-specific value provided by the ACS to indicate the results of the attempt to authenticate
    /// the Cardholder.
    pub eci: Option<String>,

    /// The received Results Request from the Directory Server.
    pub results_request: Option<serde_json::Value>,

    /// The sent Results Response to the Directory Server.
    pub results_response: Option<serde_json::Value>,

    /// Optional object containing error details if any errors occurred during the process.
    pub error_details: Option<NetceteraErrorDetails>,
}


pub struct BankOfAmericaRsyncResponse {
    id: String,
    application_information: Option<RsyncApplicationInformation>,
    error_information: Option<BankOfAmericaErrorInformation>,
}


pub struct SurchargeDetailsOutput {
    pub surcharge: SurchargeOutput,
    pub tax_on_surcharge: Option<Percentage<SURCHARGE_PERCENTAGE_PRECISION_LENGTH>>,
}


pub struct DisputeListConstraints {
    pub dispute_id: Option<String>,
    pub payment_id: Option<common_utils::id_type::PaymentId>,
    pub limit: Option<u32>,
    pub offset: Option<u32>,
    pub profile_id: Option<Vec<common_utils::id_type::ProfileId>>,
    pub dispute_status: Option<Vec<common_enums::DisputeStatus>>,
    pub dispute_stage: Option<Vec<common_enums::DisputeStage>>,
    pub reason: Option<String>,
    pub connector: Option<Vec<String>>,
    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
    pub currency: Option<Vec<common_enums::Currency>>,
    pub time_range: Option<common_utils::types::TimeRange>,
}


pub struct CapturePaymentData {
    #[serde(rename = "merchant-account-id")]
    pub merchant_account_id: MerchantAccountId,
    #[serde(rename = "request-id")]
    pub request_id: String,
    #[serde(rename = "transaction-type")]
    pub transaction_type: GetnetTransactionType,
    #[serde(rename = "parent-transaction-id")]
    pub parent_transaction_id: String,
    #[serde(rename = "requested-amount")]
    pub requested_amount: Amount,
    pub notifications: NotificationContainer,
    #[serde(rename = "ip-address")]
    pub ip_address: Option<Secret<String, IpAddress>>,
}


impl FraudCheckInterface for KafkaStore {
    async fn insert_fraud_check_response(
        &self,
        new: storage::FraudCheckNew,
    ) -> CustomResult<FraudCheck, StorageError> {
        let frm = self.diesel_store.insert_fraud_check_response(new).await?;
        if let Err(er) = self
            .kafka_producer
            .log_fraud_check(&frm, None, self.tenant_id.clone())
            .await
        {
            logger::error!(message = "Failed to log analytics event for fraud check", error_message = ?er);
        }
        Ok(frm)
    }
    async fn update_fraud_check_response_with_attempt_id(
        &self,
        this: FraudCheck,
        fraud_check: FraudCheckUpdate,
    ) -> CustomResult<FraudCheck, StorageError> {
        let frm = self
            .diesel_store
            .update_fraud_check_response_with_attempt_id(this, fraud_check)
            .await?;
        if let Err(er) = self
            .kafka_producer
            .log_fraud_check(&frm, None, self.tenant_id.clone())
            .await
        {
            logger::error!(message="Failed to log analytics event for fraud check {frm:?}", error_message=?er)
        }
        Ok(frm)
    }
    async fn find_fraud_check_by_payment_id(
        &self,
        payment_id: id_type::PaymentId,
        merchant_id: id_type::MerchantId,
    ) -> CustomResult<FraudCheck, StorageError> {
        let frm = self
            .diesel_store
            .find_fraud_check_by_payment_id(payment_id, merchant_id)
            .await?;
        if let Err(er) = self
            .kafka_producer
            .log_fraud_check(&frm, None, self.tenant_id.clone())
            .await
        {
            logger::error!(message="Failed to log analytics event for fraud check {frm:?}", error_message=?er)
        }
        Ok(frm)
    }
    async fn find_fraud_check_by_payment_id_if_present(
        &self,
        payment_id: id_type::PaymentId,
        merchant_id: id_type::MerchantId,
    ) -> CustomResult<Option<FraudCheck>, StorageError> {
        let frm = self
            .diesel_store
            .find_fraud_check_by_payment_id_if_present(payment_id, merchant_id)
            .await?;

        if let Some(fraud_check) = frm.clone() {
            if let Err(er) = self
                .kafka_producer
                .log_fraud_check(&fraud_check, None, self.tenant_id.clone())
                .await
            {
                logger::error!(message="Failed to log analytics event for frm {frm:?}", error_message=?er);
            }
        }
        Ok(frm)
    }
}

impl FraudCheckInterface for for KafkaStore


pub fn get_min_entity(
    user_entity: EntityType,
    filter_entity: Option<EntityType>,
) -> UserResult<EntityType> {
    let Some(filter_entity) = filter_entity else {
        return Ok(user_entity);
    };

    if user_entity < filter_entity {
        return Err(report!(UserErrors::InvalidRoleOperation)).attach_printable(format!(
            "{user_entity} level user requesting data for {filter_entity:?} level",
        ));
    }

    Ok(cmp::min(user_entity, filter_entity))
}


/// Get all the variants of a enum in the form of a string
pub fn get_possible_values_for_enum<T>() -> String
where
    T: strum::IntoEnumIterator + ToString,
{
    T::iter()
        .map(|variants| variants.to_string())
        .collect::<Vec<_>>()
        .join(", ")
}


impl MerchantAccountCreate {
    pub fn get_merchant_reference_id(&self) -> id_type::MerchantId {
        id_type::MerchantId::from_merchant_name(self.merchant_name.clone().expose())
    }

    pub fn get_merchant_details_as_secret(
        &self,
    ) -> CustomResult<Option<pii::SecretSerdeValue>, errors::ParsingError> {
        self.merchant_details
            .as_ref()
            .map(|merchant_details| merchant_details.encode_to_value().map(Secret::new))
            .transpose()
    }

    pub fn get_metadata_as_secret(
        &self,
    ) -> CustomResult<Option<pii::SecretSerdeValue>, errors::ParsingError> {
        self.metadata
            .as_ref()
            .map(|metadata| metadata.encode_to_value().map(Secret::new))
            .transpose()
    }

    pub fn get_primary_details_as_value(
        &self,
    ) -> CustomResult<serde_json::Value, errors::ParsingError> {
        Vec::<PrimaryBusinessDetails>::new().encode_to_value()
    }
}


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


impl api::PayoutFulfill for Stripe {}


pub async fn customer_delete(
    state: web::Data<routes::AppState>,
    req: HttpRequest,
    path: web::Path<id_type::CustomerId>,
) -> HttpResponse {
    let customer_id = path.into_inner();

    let flow = Flow::CustomersDelete;

    Box::pin(wrap::compatibility_api_wrap::<
        _,
        _,
        _,
        _,
        _,
        types::CustomerDeleteResponse,
        errors::StripeErrorCode,
        _,
    >(
        flow,
        state.into_inner(),
        &req,
        customer_id,
        |state, auth: auth::AuthenticationData, customer_id, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            customers::delete_customer(state, merchant_context, customer_id)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl RoutingApproach {
    pub fn from_decision_engine_approach(approach: &str) -> Self {
        match approach {
            "SR_SELECTION_V3_ROUTING" => Self::Exploitation,
            "SR_V3_HEDGING" => Self::Exploration,
            _ => Self::Default,
        }
    }
}


/// Perform health check for all services involved
    pub async fn perform_health_check(
        &self,
        config: &GrpcClientSettings,
    ) -> HealthCheckResult<HealthCheckMap> {
        let dynamic_routing_config = &config.dynamic_routing_client;
        let connection = match dynamic_routing_config {
            Some(DynamicRoutingClientConfig::Enabled {
                host,
                port,
                service,
            }) => Some((host.clone(), *port, service.clone())),
            _ => None,
        };

        let health_client = self
            .clients
            .get(&HealthCheckServices::DynamicRoutingService);

        // SAFETY : This is a safe cast as there exists a valid
        // integer value for this variant
        #[allow(clippy::as_conversions)]
        let expected_status = ServingStatus::Serving as i32;

        let mut service_map = HealthCheckMap::new();

        let health_check_succeed = connection
            .as_ref()
            .async_map(|conn| self.get_response_from_grpc_service(conn.2.clone(), health_client))
            .await
            .transpose()
            .change_context(HealthCheckError::ConnectionError(
                "error calling dynamic routing service".to_string(),
            ))
            .map_err(|err| logger::error!(error=?err))
            .ok()
            .flatten()
            .is_some_and(|resp| resp.status == expected_status);

        connection.and_then(|_conn| {
            service_map.insert(
                HealthCheckServices::DynamicRoutingService,
                health_check_succeed,
            )
        });

        Ok(service_map)
    }


pub trait ApiModelToDieselModelConvertor<F> {
    /// Convert from a foreign type to the current type
    fn convert_from(from: F) -> Self;
    fn convert_back(self) -> F;
}


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

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Minor
        //    TODO! Check connector documentation, on which unit they are processing the currency.
        //    If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor,
        //    if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base
    }

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

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = sift::SiftAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::AUTHORIZATION.to_string(),
            auth.api_key.expose().into_masked(),
        )])
    }

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.code,
            message: response.message,
            reason: response.reason,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


pub async fn trigger_refund_to_gateway(
    state: &SessionState,
    refund: &diesel_refund::Refund,
    merchant_context: &domain::MerchantContext,
    payment_attempt: &storage::PaymentAttempt,
    payment_intent: &storage::PaymentIntent,
    creds_identifier: Option<String>,
    split_refunds: Option<SplitRefundsRequest>,
) -> RouterResult<diesel_refund::Refund> {
    let routed_through = payment_attempt
        .connector
        .clone()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to retrieve connector from payment attempt")?;

    let storage_scheme = merchant_context.get_merchant_account().storage_scheme;
    metrics::REFUND_COUNT.add(
        1,
        router_env::metric_attributes!(("connector", routed_through.clone())),
    );

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

    let currency = payment_attempt.currency.ok_or_else(|| {
        report!(errors::ApiErrorResponse::InternalServerError).attach_printable(
            "Transaction in invalid. Missing field \"currency\" in payment_attempt.",
        )
    })?;

    validator::validate_for_valid_refunds(payment_attempt, connector.connector_name)?;

    let mut router_data = core_utils::construct_refund_router_data(
        state,
        &routed_through,
        merchant_context,
        (payment_attempt.get_total_amount(), currency),
        payment_intent,
        payment_attempt,
        refund,
        creds_identifier.clone(),
        split_refunds,
    )
    .await?;

    let add_access_token_result = Box::pin(access_token::add_access_token(
        state,
        &connector,
        merchant_context,
        &router_data,
        creds_identifier.as_deref(),
    ))
    .await?;

    logger::debug!(refund_router_data=?router_data);

    access_token::update_router_data_with_access_token_result(
        &add_access_token_result,
        &mut router_data,
        &payments::CallConnectorAction::Trigger,
    );

    let router_data_res = if !(add_access_token_result.connector_supports_access_token
        && router_data.access_token.is_none())
    {
        let connector_integration: services::BoxedRefundConnectorIntegrationInterface<
            api::Execute,
            types::RefundsData,
            types::RefundsResponseData,
        > = connector.connector.get_connector_integration();
        let router_data_res = services::execute_connector_processing_step(
            state,
            connector_integration,
            &router_data,
            payments::CallConnectorAction::Trigger,
            None,
            None,
        )
        .await;
        let option_refund_error_update =
            router_data_res
                .as_ref()
                .err()
                .and_then(|error| match error.current_context() {
                    errors::ConnectorError::NotImplemented(message) => {
                        Some(diesel_refund::RefundUpdate::ErrorUpdate {
                            refund_status: Some(enums::RefundStatus::Failure),
                            refund_error_message: Some(
                                errors::ConnectorError::NotImplemented(message.to_owned())
                                    .to_string(),
                            ),
                            refund_error_code: Some("NOT_IMPLEMENTED".to_string()),
                            updated_by: storage_scheme.to_string(),
                            connector_refund_id: None,
                            processor_refund_data: None,
                            unified_code: None,
                            unified_message: None,
                            issuer_error_code: None,
                            issuer_error_message: None,
                        })
                    }
                    errors::ConnectorError::NotSupported { message, connector } => {
                        Some(diesel_refund::RefundUpdate::ErrorUpdate {
                            refund_status: Some(enums::RefundStatus::Failure),
                            refund_error_message: Some(format!(
                                "{message} is not supported by {connector}"
                            )),
                            refund_error_code: Some("NOT_SUPPORTED".to_string()),
                            updated_by: storage_scheme.to_string(),
                            connector_refund_id: None,
                            processor_refund_data: None,
                            unified_code: None,
                            unified_message: None,
                            issuer_error_code: None,
                            issuer_error_message: None,
                        })
                    }
                    _ => None,
                });
        // Update the refund status as failure if connector_error is NotImplemented
        if let Some(refund_error_update) = option_refund_error_update {
            state
                .store
                .update_refund(
                    refund.to_owned(),
                    refund_error_update,
                    merchant_context.get_merchant_account().storage_scheme,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::InternalServerError)
                .attach_printable_lazy(|| {
                    format!(
                        "Failed while updating refund: refund_id: {}",
                        refund.refund_id
                    )
                })?;
        }
        let mut refund_router_data_res = router_data_res.to_refund_failed_response()?;
        // Initiating Integrity check
        let integrity_result = check_refund_integrity(
            &refund_router_data_res.request,
            &refund_router_data_res.response,
        );
        refund_router_data_res.integrity_check = integrity_result;
        refund_router_data_res
    } else {
        router_data
    };

    let refund_update = match router_data_res.response {
        Err(err) => {
            let option_gsm = helpers::get_gsm_record(
                state,
                Some(err.code.clone()),
                Some(err.message.clone()),
                connector.connector_name.to_string(),
                consts::REFUND_FLOW_STR.to_string(),
            )
            .await;
            // Note: Some connectors do not have a separate list of refund errors
            // In such cases, the error codes and messages are stored under "Authorize" flow in GSM table
            // So we will have to fetch the GSM using Authorize flow in case GSM is not found using "refund_flow"
            let option_gsm = if option_gsm.is_none() {
                helpers::get_gsm_record(
                    state,
                    Some(err.code.clone()),
                    Some(err.message.clone()),
                    connector.connector_name.to_string(),
                    consts::AUTHORIZE_FLOW_STR.to_string(),
                )
                .await
            } else {
                option_gsm
            };

            let gsm_unified_code = option_gsm.as_ref().and_then(|gsm| gsm.unified_code.clone());
            let gsm_unified_message = option_gsm.and_then(|gsm| gsm.unified_message);

            let (unified_code, unified_message) = if let Some((code, message)) =
                gsm_unified_code.as_ref().zip(gsm_unified_message.as_ref())
            {
                (code.to_owned(), message.to_owned())
            } else {
                (
                    consts::DEFAULT_UNIFIED_ERROR_CODE.to_owned(),
                    consts::DEFAULT_UNIFIED_ERROR_MESSAGE.to_owned(),
                )
            };

            diesel_refund::RefundUpdate::ErrorUpdate {
                refund_status: Some(enums::RefundStatus::Failure),
                refund_error_message: err.reason.or(Some(err.message)),
                refund_error_code: Some(err.code),
                updated_by: storage_scheme.to_string(),
                connector_refund_id: None,
                processor_refund_data: None,
                unified_code: Some(unified_code),
                unified_message: Some(unified_message),
                issuer_error_code: err.network_decline_code,
                issuer_error_message: err.network_error_message,
            }
        }
        Ok(response) => {
            // match on connector integrity checks
            match router_data_res.integrity_check.clone() {
                Err(err) => {
                    let (refund_connector_transaction_id, processor_refund_data) =
                        err.connector_transaction_id.map_or((None, None), |txn_id| {
                            let (refund_id, refund_data) =
                                ConnectorTransactionId::form_id_and_data(txn_id);
                            (Some(refund_id), refund_data)
                        });
                    metrics::INTEGRITY_CHECK_FAILED.add(
                        1,
                        router_env::metric_attributes!(
                            ("connector", connector.connector_name.to_string()),
                            (
                                "merchant_id",
                                merchant_context.get_merchant_account().get_id().clone()
                            ),
                        ),
                    );
                    diesel_refund::RefundUpdate::ErrorUpdate {
                        refund_status: Some(enums::RefundStatus::ManualReview),
                        refund_error_message: Some(format!(
                            "Integrity Check Failed! as data mismatched for fields {}",
                            err.field_names
                        )),
                        refund_error_code: Some("IE".to_string()),
                        updated_by: storage_scheme.to_string(),
                        connector_refund_id: refund_connector_transaction_id,
                        processor_refund_data,
                        unified_code: None,
                        unified_message: None,
                        issuer_error_code: None,
                        issuer_error_message: None,
                    }
                }
                Ok(()) => {
                    if response.refund_status == diesel_models::enums::RefundStatus::Success {
                        metrics::SUCCESSFUL_REFUND.add(
                            1,
                            router_env::metric_attributes!((
                                "connector",
                                connector.connector_name.to_string(),
                            )),
                        )
                    }
                    let (connector_refund_id, processor_refund_data) =
                        ConnectorTransactionId::form_id_and_data(response.connector_refund_id);
                    diesel_refund::RefundUpdate::Update {
                        connector_refund_id,
                        refund_status: response.refund_status,
                        sent_to_gateway: true,
                        refund_error_message: None,
                        refund_arn: "".to_string(),
                        updated_by: storage_scheme.to_string(),
                        processor_refund_data,
                    }
                }
            }
        }
    };

    let response = state
        .store
        .update_refund(
            refund.to_owned(),
            refund_update,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!(
                "Failed while updating refund: refund_id: {}",
                refund.refund_id
            )
        })?;
    utils::trigger_refund_outgoing_webhook(
        state,
        merchant_context,
        &response,
        payment_attempt.profile_id.clone(),
    )
    .await
    .map_err(|error| logger::warn!(refunds_outgoing_webhook_error=?error))
    .ok();
    Ok(response)
}