                state,
                req_state,
                merchant_context,
                &profile,
                operation,
                payment_sync_request,
                get_tracker_response,
                call_connector_action,
                HeaderPayload::default(),
            ))
            .await?;

        Ok(router_types::RedirectPaymentFlowResponse {
            payment_data,
            profile,
        })
    }
    fn generate_response(
        &self,
        payment_flow_response: &Self::PaymentFlowResponse,
    ) -> RouterResult<services::ApplicationResponse<api::RedirectionResponse>> {
        let payment_intent = &payment_flow_response.payment_data.payment_intent;
        let profile = &payment_flow_response.profile;

        let return_url = payment_intent
            .return_url
            .as_ref()
            .or(profile.return_url.as_ref())
            .ok_or(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("return url not found in payment intent and profile")?
            .to_owned();

        let return_url = return_url
            .add_query_params(("id", payment_intent.id.get_string_repr()))
            .add_query_params(("status", &payment_intent.status.to_string()));

        let return_url_str = return_url.into_inner().to_string();

        Ok(services::ApplicationResponse::JsonForRedirection(
            api::RedirectionResponse {
                return_url_with_query_params: return_url_str,
            },
        ))
    }

    fn get_payment_action(&self) -> services::PaymentAction {
        services::PaymentAction::PSync
    }
}

#[derive(Clone, Debug)]
pub struct PaymentAuthenticateCompleteAuthorize;

#[cfg(feature = "v1")]
#[async_trait::async_trait]
impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize {
    type PaymentFlowResponse = router_types::AuthenticatePaymentFlowResponse;

    #[allow(clippy::too_many_arguments)]
    async fn call_payment_flow(
        &self,
        state: &SessionState,
        req_state: ReqState,
        merchant_context: domain::MerchantContext,
        req: PaymentsRedirectResponseData,
        connector_action: CallConnectorAction,
        connector: String,
        payment_id: id_type::PaymentId,
    ) -> RouterResult<Self::PaymentFlowResponse> {
        let merchant_id = merchant_context.get_merchant_account().get_id().clone();
        let key_manager_state = &state.into();

        let payment_intent = state
            .store
            .find_payment_intent_by_payment_id_merchant_id(
                key_manager_state,
                &payment_id,
                &merchant_id,
                merchant_context.get_merchant_key_store(),
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
        let payment_attempt = state
            .store
            .find_payment_attempt_by_attempt_id_merchant_id(
                &payment_intent.active_attempt.get_id(),
                &merchant_id,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
        let authentication_id = payment_attempt
            .authentication_id
            .ok_or(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("missing authentication_id in payment_attempt")?;
        let authentication = state
            .store
            .find_authentication_by_merchant_id_authentication_id(&merchant_id, &authentication_id)
            .await
            .to_not_found_response(errors::ApiErrorResponse::AuthenticationNotFound {
                id: authentication_id.get_string_repr().to_string(),
            })?;
        // Fetching merchant_connector_account to check if pull_mechanism is enabled for 3ds connector
        let authentication_merchant_connector_account = helpers::get_merchant_connector_account(
            state,
            &merchant_id,
            None,
            merchant_context.get_merchant_key_store(),
            &payment_intent
                .profile_id
                .ok_or(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("missing profile_id in payment_intent")?,
            &payment_attempt
                .authentication_connector
                .ok_or(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("missing authentication connector in payment_intent")?,
            None,
        )
        .await?;
        let is_pull_mechanism_enabled =
            utils::check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata(
                authentication_merchant_connector_account
                    .get_metadata()
                    .map(|metadata| metadata.expose()),
            );
        let response = if is_pull_mechanism_enabled
            || authentication.authentication_type
                != Some(common_enums::DecoupledAuthenticationType::Challenge)
        {
            let payment_confirm_req = api::PaymentsRequest {
                payment_id: Some(req.resource_id.clone()),
                merchant_id: req.merchant_id.clone(),
                feature_metadata: Some(api_models::payments::FeatureMetadata {
                    redirect_response: Some(api_models::payments::RedirectResponse {
                        param: req.param.map(Secret::new),
                        json_payload: Some(
                            req.json_payload.unwrap_or(serde_json::json!({})).into(),
                        ),
                    }),
                    search_tags: None,
                    apple_pay_recurring_details: None,
                }),
                ..Default::default()
            };
            Box::pin(payments_core::<
                api::Authorize,
                api::PaymentsResponse,
                _,
                _,
                _,
                _,
            >(
                state.clone(),
                req_state,
                merchant_context.clone(),
                None,
                PaymentConfirm,
                payment_confirm_req,
                services::api::AuthFlow::Merchant,
                connector_action,
                None,
                HeaderPayload::with_source(enums::PaymentSource::ExternalAuthenticator),
            ))
            .await?
        } else {
            let payment_sync_req = api::PaymentsRetrieveRequest {
                resource_id: req.resource_id,
                merchant_id: req.merchant_id,
                param: req.param,
                force_sync: req.force_sync,
                connector: req.connector,
                merchant_connector_details: req.creds_identifier.map(|creds_id| {
                    api::MerchantConnectorDetailsWrap {
                        creds_identifier: creds_id,
                        encoded_data: None,
                    }
                }),
                client_secret: None,
                expand_attempts: None,
                expand_captures: None,
                all_keys_required: None,
            };
            Box::pin(
                payments_core::<api::PSync, api::PaymentsResponse, _, _, _, _>(
                    state.clone(),
                    req_state,
                    merchant_context.clone(),
                    None,
                    PaymentStatus,
                    payment_sync_req,
                    services::api::AuthFlow::Merchant,
                    connector_action,
                    None,
                    HeaderPayload::default(),
                ),
            )
            .await?
        };
        let payments_response = match response {
            services::ApplicationResponse::Json(response) => Ok(response),
            services::ApplicationResponse::JsonWithHeaders((response, _)) => Ok(response),
            _ => Err(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to get the response in json"),
        }?;
        // When intent status is RequiresCustomerAction, Set poll_id in redis to allow the fetch status of poll through retrieve_poll_status api from client
        if payments_response.status == common_enums::IntentStatus::RequiresCustomerAction {
            let req_poll_id = core_utils::get_external_authentication_request_poll_id(&payment_id);
            let poll_id = core_utils::get_poll_id(&merchant_id, req_poll_id.clone());
            let redis_conn = state
                .store
                .get_redis_conn()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to get redis connection")?;
            redis_conn
                .set_key_with_expiry(
                    &poll_id.into(),
                    api_models::poll::PollStatus::Pending.to_string(),
                    consts::POLL_ID_TTL,
                )
                .await
                .change_context(errors::StorageError::KVError)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to add poll_id in redis")?;
        };
        let default_poll_config = router_types::PollConfig::default();
        let default_config_str = default_poll_config
            .encode_to_string_of_json()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error while stringifying default poll config")?;
        let poll_config = state
            .store
            .find_config_by_key_unwrap_or(
                &router_types::PollConfig::get_poll_config_key(connector),
                Some(default_config_str),
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("The poll config was not found in the DB")?;
        let poll_config: router_types::PollConfig = poll_config
            .config
            .parse_struct("PollConfig")
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error while parsing PollConfig")?;
        let profile_id = payments_response
            .profile_id
            .as_ref()
            .get_required_value("profile_id")?;
        let business_profile = state
            .store
            .find_business_profile_by_profile_id(
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                profile_id,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
                id: profile_id.get_string_repr().to_owned(),
            })?;
        Ok(router_types::AuthenticatePaymentFlowResponse {
            payments_response,
            poll_config,
            business_profile,
        })
    }
    fn generate_response(
        &self,
        payment_flow_response: &Self::PaymentFlowResponse,
        payment_id: id_type::PaymentId,
        connector: String,
    ) -> RouterResult<services::ApplicationResponse<api::RedirectionResponse>> {
        let payments_response = &payment_flow_response.payments_response;
        let redirect_response = helpers::get_handle_response_url(
            payment_id.clone(),
            &payment_flow_response.business_profile,
            payments_response,
            connector.clone(),
        )?;
        // html script to check if inside iframe, then send post message to parent for redirection else redirect self to return_url
        let html = core_utils::get_html_redirect_response_for_external_authentication(
            redirect_response.return_url_with_query_params,
            payments_response,
            payment_id,
            &payment_flow_response.poll_config,
        )?;
        Ok(services::ApplicationResponse::Form(Box::new(
            services::RedirectionFormData {
                redirect_form: services::RedirectForm::Html { html_data: html },
                payment_method_data: None,
                amount: payments_response.amount.to_string(),
                currency: payments_response.currency.clone(),
            },
        )))
    }

    fn get_payment_action(&self) -> services::PaymentAction {
        services::PaymentAction::PaymentAuthenticateCompleteAuthorize
    }
}

#[cfg(feature = "v1")]
pub async fn get_decrypted_wallet_payment_method_token<F, Req, D>(
    operation: &BoxedOperation<'_, F, Req, D>,
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_data: &mut D,
    connector_call_type_optional: Option<&ConnectorCallType>,
) -> CustomResult<Option<PaymentMethodToken>, errors::ApiErrorResponse>
where
    F: Send + Clone + Sync,
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    if is_operation_confirm(operation)
        && payment_data.get_payment_attempt().payment_method
            == Some(storage_enums::PaymentMethod::Wallet)
    {
        let wallet_type = payment_data
            .get_payment_attempt()
            .payment_method_type
            .get_required_value("payment_method_type")?;

        let wallet: Box<dyn WalletFlow<F, D>> = match wallet_type {
            storage_enums::PaymentMethodType::ApplePay => Box::new(ApplePayWallet),
            storage_enums::PaymentMethodType::Paze => Box::new(PazeWallet),
            storage_enums::PaymentMethodType::GooglePay => Box::new(GooglePayWallet),
            _ => return Ok(None),
        };

        // Check if the wallet has already decrypted the token from the payment data.
        // If a pre-decrypted token is available, use it directly to avoid redundant decryption.
        if let Some(predecrypted_token) = wallet.check_predecrypted_token(payment_data)? {
            logger::debug!("Using predecrypted token for wallet");
            return Ok(Some(predecrypted_token));
        }

        let merchant_connector_account =
            get_merchant_connector_account_for_wallet_decryption_flow::<F, D>(
                state,
                merchant_context,
                payment_data,
                connector_call_type_optional,
            )
            .await?;

        let decide_wallet_flow = &wallet
            .decide_wallet_flow(state, payment_data, &merchant_connector_account)
            .attach_printable("Failed to decide wallet flow")?
            .async_map(|payment_price_data| async move {
                wallet
                    .decrypt_wallet_token(&payment_price_data, payment_data)
                    .await
            })
            .await
            .transpose()
            .attach_printable("Failed to decrypt Wallet token")?;
        Ok(decide_wallet_flow.clone())
    } else {
        Ok(None)
    }
}

#[cfg(feature = "v1")]
pub async fn get_merchant_connector_account_for_wallet_decryption_flow<F, D>(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payment_data: &mut D,
    connector_call_type_optional: Option<&ConnectorCallType>,
) -> RouterResult<helpers::MerchantConnectorAccountType>
where
    F: Send + Clone + Sync,
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    let connector_call_type = connector_call_type_optional
        .get_required_value("connector_call_type")
        .change_context(errors::ApiErrorResponse::InternalServerError)?;
    let connector_routing_data = match connector_call_type {
        ConnectorCallType::PreDetermined(connector_routing_data) => connector_routing_data,
        ConnectorCallType::Retryable(connector_routing_data) => connector_routing_data
            .first()
            .ok_or(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Found no connector routing data in retryable call")?,
        ConnectorCallType::SessionMultiple(_session_connector_data) => {
            return Err(errors::ApiErrorResponse::InternalServerError).attach_printable(
                "SessionMultiple connector call type is invalid in confirm calls",
            );
        }
    };

    construct_profile_id_and_get_mca(
        state,
        merchant_context,
        payment_data,
        &connector_routing_data
            .connector_data
            .connector_name
            .to_string(),
        connector_routing_data
            .connector_data
            .merchant_connector_id
            .as_ref(),
        false,
    )
    .await
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn call_connector_service<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    call_connector_action: CallConnectorAction,
    validate_result: &operations::ValidateResult,
    schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    is_retry_payment: bool,
    return_raw_connector_response: Option<bool>,
    merchant_connector_account: helpers::MerchantConnectorAccountType,
    mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    tokenization_action: TokenizationAction,
) -> RouterResult<(
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    helpers::MerchantConnectorAccountType,
)>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let add_access_token_result = router_data
        .add_access_token(
            state,
            &connector,
            merchant_context,
            payment_data.get_creds_identifier(),
        )
        .await?;

    router_data = router_data.add_session_token(state, &connector).await?;

    let should_continue_further = access_token::update_router_data_with_access_token_result(
        &add_access_token_result,
        &mut router_data,
        &call_connector_action,
    );

    let should_continue_further = match router_data
        .create_order_at_connector(state, &connector, should_continue_further)
        .await?
    {
        Some(create_order_response) => {
            if let Ok(order_id) = create_order_response.clone().create_order_result {
                payment_data.set_connector_response_reference_id(Some(order_id.clone()))
            }

            // Set the response in routerdata response to carry forward
            router_data
                .update_router_data_with_create_order_response(create_order_response.clone());
            create_order_response.create_order_result.ok().is_some()
        }
        // If create order is not required, then we can proceed with further processing
        None => true,
    };

    let updated_customer = call_create_connector_customer_if_required(
        state,
        customer,
        merchant_context,
        &merchant_connector_account,
        payment_data,
        router_data.access_token.as_ref(),
    )
    .await?;

    #[cfg(feature = "v1")]
    if let Some(connector_customer_id) = payment_data.get_connector_customer_id() {
        router_data.connector_customer = Some(connector_customer_id);
    }

    router_data.payment_method_token = payment_data.get_payment_method_token().cloned();

    let payment_method_token_response = router_data
        .add_payment_method_token(
            state,
            &connector,
            &tokenization_action,
            should_continue_further,
        )
        .await?;

    let mut should_continue_further =
        tokenization::update_router_data_with_payment_method_token_result(
            payment_method_token_response,
            &mut router_data,
            is_retry_payment,
            should_continue_further,
        );

    (router_data, should_continue_further) = complete_preprocessing_steps_if_required(
        state,
        &connector,
        payment_data,
        router_data,
        operation,
        should_continue_further,
    )
    .await?;

    if let Ok(router_types::PaymentsResponseData::PreProcessingResponse {
        session_token: Some(session_token),
        ..
    }) = router_data.response.to_owned()
    {
        payment_data.push_sessions_token(session_token);
    };

    // In case of authorize flow, pre-task and post-tasks are being called in build request
    // if we do not want to proceed further, then the function will return Ok(None, false)
    let (connector_request, should_continue_further) = if should_continue_further {
        // Check if the actual flow specific request can be built with available data
        router_data
            .build_flow_specific_connector_request(state, &connector, call_connector_action.clone())
            .await?
    } else {
        (None, false)
    };

    if should_add_task_to_process_tracker(payment_data) {
        operation
            .to_domain()?
            .add_task_to_process_tracker(
                state,
                payment_data.get_payment_attempt(),
                validate_result.requeue,
                schedule_time,
            )
            .await
            .map_err(|error| logger::error!(process_tracker_error=?error))
            .ok();
    }

    // Update the payment trackers just before calling the connector
    // Since the request is already built in the previous step,
    // there should be no error in request construction from hyperswitch end
    (_, *payment_data) = operation
        .to_update_tracker()?
        .update_trackers(
            state,
            req_state,
            payment_data.clone(),
            customer.clone(),
            merchant_context.get_merchant_account().storage_scheme,
            updated_customer,
            merchant_context.get_merchant_key_store(),
            frm_suggestion,
            header_payload.clone(),
        )
        .await?;

    let router_data = if should_continue_further {
        // The status of payment_attempt and intent will be updated in the previous step
        // update this in router_data.
        // This is added because few connector integrations do not update the status,
        // and rely on previous status set in router_data
        router_data.status = payment_data.get_payment_attempt().status;
        router_data
            .decide_flows(
                state,
                &connector,
                call_connector_action,
                connector_request,
                business_profile,
                header_payload.clone(),
                return_raw_connector_response,
            )
            .await
    } else {
        Ok(router_data)
    }?;

    Ok((router_data, merchant_connector_account))
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn call_connector_service_prerequisites<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    validate_result: &operations::ValidateResult,
    business_profile: &domain::Profile,
    should_retry_with_pan: bool,
    routing_decision: Option<routing_helpers::RoutingDecisionData>,
) -> RouterResult<(
    helpers::MerchantConnectorAccountType,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    TokenizationAction,
)>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let merchant_connector_account = construct_profile_id_and_get_mca(
        state,
        merchant_context,
        payment_data,
        &connector.connector_name.to_string(),
        connector.merchant_connector_id.as_ref(),
        false,
    )
    .await?;

    let customer_acceptance = payment_data
        .get_payment_attempt()
        .customer_acceptance
        .clone();

    if is_pre_network_tokenization_enabled(
        state,
        business_profile,
        customer_acceptance,
        connector.connector_name,
    ) {
        let payment_method_data = payment_data.get_payment_method_data();
        let customer_id = payment_data.get_payment_intent().customer_id.clone();
        if let (Some(domain::PaymentMethodData::Card(card_data)), Some(customer_id)) =
            (payment_method_data, customer_id)
        {
            let vault_operation =
                get_vault_operation_for_pre_network_tokenization(state, customer_id, card_data)
                    .await;
            match vault_operation {
                payments::VaultOperation::SaveCardAndNetworkTokenData(
                    card_and_network_token_data,
                ) => {
                    payment_data.set_vault_operation(
                        payments::VaultOperation::SaveCardAndNetworkTokenData(Box::new(
                            *card_and_network_token_data.clone(),
                        )),
                    );

                    payment_data.set_payment_method_data(Some(
                        domain::PaymentMethodData::NetworkToken(
                            card_and_network_token_data
                                .network_token
                                .network_token_data
                                .clone(),
                        ),
                    ));
                }
                payments::VaultOperation::SaveCardData(card_data_for_vault) => payment_data
                    .set_vault_operation(payments::VaultOperation::SaveCardData(
                        card_data_for_vault.clone(),
                    )),
                payments::VaultOperation::ExistingVaultData(_) => (),
            }
        }
    }

    if payment_data
        .get_payment_attempt()
        .merchant_connector_id
        .is_none()
    {
        payment_data.set_merchant_connector_id_in_attempt(merchant_connector_account.get_mca_id());
    }

    operation
        .to_domain()?
        .populate_payment_data(
            state,
            payment_data,
            merchant_context,
            business_profile,
            &connector,
        )
        .await?;

    let (pd, tokenization_action) = get_connector_tokenization_action_when_confirm_true(
        state,
        operation,
        payment_data,
        validate_result,
        merchant_context.get_merchant_key_store(),
        customer,
        business_profile,
        should_retry_with_pan,
    )
    .await?;
    *payment_data = pd;

    // This is used to apply any kind of routing decision to the required data,
    // before the call to `connector` is made.
    routing_decision.map(|decision| decision.apply_routing_decision(payment_data));

    // Validating the blocklist guard and generate the fingerprint
    blocklist_guard(state, merchant_context, operation, payment_data).await?;

    let merchant_recipient_data = payment_data
        .get_merchant_recipient_data(
            state,
            merchant_context,
            &merchant_connector_account,
            &connector,
        )
        .await?;

    let router_data = payment_data
        .construct_router_data(
            state,
            connector.connector.id(),
            merchant_context,
            customer,
            &merchant_connector_account,
            merchant_recipient_data,
            None,
        )
        .await?;

    let connector_request_reference_id = router_data.connector_request_reference_id.clone();
    payment_data
        .set_connector_request_reference_id_in_payment_attempt(connector_request_reference_id);

    Ok((merchant_connector_account, router_data, tokenization_action))
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn decide_unified_connector_service_call<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    call_connector_action: CallConnectorAction,
    validate_result: &operations::ValidateResult,
    schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    is_retry_payment: bool,
    all_keys_required: Option<bool>,
    merchant_connector_account: helpers::MerchantConnectorAccountType,
    mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    tokenization_action: TokenizationAction,
) -> RouterResult<(
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    helpers::MerchantConnectorAccountType,
)>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    record_time_taken_with(|| async {
        if !matches!(
            call_connector_action,
            CallConnectorAction::UCSHandleResponse(_)
        ) && !matches!(
            call_connector_action,
            CallConnectorAction::HandleResponse(_),
        ) && should_call_unified_connector_service(
            state,
            merchant_context,
            &router_data,
            Some(payment_data),
        )
        .await?
        {
            router_env::logger::info!(
                "Processing payment through UCS gateway system - payment_id={}, attempt_id={}",
                payment_data
                    .get_payment_intent()
                    .payment_id
                    .get_string_repr(),
                payment_data.get_payment_attempt().attempt_id
            );
            if should_add_task_to_process_tracker(payment_data) {
                operation
                    .to_domain()?
                    .add_task_to_process_tracker(
                        state,
                        payment_data.get_payment_attempt(),
                        validate_result.requeue,
                        schedule_time,
                    )
                    .await
                    .map_err(|error| logger::error!(process_tracker_error=?error))
                    .ok();
            }

            // Update feature metadata to track UCS usage for stickiness
            update_gateway_system_in_feature_metadata(
                payment_data,
                GatewaySystem::UnifiedConnectorService,
            )?;

            (_, *payment_data) = operation
                .to_update_tracker()?
                .update_trackers(
                    state,
                    req_state,
                    payment_data.clone(),
                    customer.clone(),
                    merchant_context.get_merchant_account().storage_scheme,
                    None,
                    merchant_context.get_merchant_key_store(),
                    frm_suggestion,
                    header_payload.clone(),
                )
                .await?;

            router_data
                .call_unified_connector_service(
                    state,
                    merchant_connector_account.clone(),
                    merchant_context,
                )
                .await?;

            Ok((router_data, merchant_connector_account))
        } else {
            router_env::logger::info!(
                "Processing payment through Direct gateway system - payment_id={}, attempt_id={}",
                payment_data
                    .get_payment_intent()
                    .payment_id
                    .get_string_repr(),
                payment_data.get_payment_attempt().attempt_id
            );

            // Update feature metadata to track Direct routing usage for stickiness
            update_gateway_system_in_feature_metadata(payment_data, GatewaySystem::Direct)?;

            call_connector_service(
                state,
                req_state,
                merchant_context,
                connector,
                operation,
                payment_data,
                customer,
                call_connector_action,
                validate_result,
                schedule_time,
                header_payload,
                frm_suggestion,
                business_profile,
                is_retry_payment,
                all_keys_required,
                merchant_connector_account,
                router_data,
                tokenization_action,
            )
            .await
        }
    })
    .await
}

async fn record_time_taken_with<F, Fut, R>(f: F) -> RouterResult<R>
where
    F: FnOnce() -> Fut,
    Fut: future::Future<Output = RouterResult<R>>,
{
    let stime = Instant::now();
    let result = f().await;
    let etime = Instant::now();
    let duration = etime.saturating_duration_since(stime);
    tracing::info!(duration = format!("Duration taken: {}", duration.as_millis()));
    result
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn call_connector_service<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    call_connector_action: CallConnectorAction,
    schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    is_retry_payment: bool,
    should_retry_with_pan: bool,
    return_raw_connector_response: Option<bool>,
    merchant_connector_account_type_details: domain::MerchantConnectorAccountTypeDetails,
    mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    updated_customer: Option<storage::CustomerUpdate>,
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let add_access_token_result = router_data
        .add_access_token(
            state,
            &connector,
            merchant_context,
            payment_data.get_creds_identifier(),
        )
        .await?;

    router_data = router_data.add_session_token(state, &connector).await?;

    let should_continue_further = access_token::update_router_data_with_access_token_result(
        &add_access_token_result,
        &mut router_data,
        &call_connector_action,
    );

    let should_continue = match router_data
        .create_order_at_connector(state, &connector, should_continue_further)
        .await?
    {
        Some(create_order_response) => {
            if let Ok(order_id) = create_order_response.clone().create_order_result {
                payment_data.set_connector_response_reference_id(Some(order_id))
            }

            // Set the response in routerdata response to carry forward
            router_data
                .update_router_data_with_create_order_response(create_order_response.clone());
            create_order_response.create_order_result.ok().map(|_| ())
        }
        // If create order is not required, then we can proceed with further processing
        None => Some(()),
    };

    // In case of authorize flow, pre-task and post-tasks are being called in build request
    // if we do not want to proceed further, then the function will return Ok(None, false)
    let (connector_request, should_continue_further) = match should_continue {
        Some(_) => {
            router_data
                .build_flow_specific_connector_request(
                    state,
                    &connector,
                    call_connector_action.clone(),
                )
                .await?
        }
        None => (None, false),
    };

    // Update the payment trackers just before calling the connector
    // Since the request is already built in the previous step,
    // there should be no error in request construction from hyperswitch end
    (_, *payment_data) = operation
        .to_update_tracker()?
        .update_trackers(
            state,
            req_state,
            payment_data.clone(),
            customer.clone(),
            merchant_context.get_merchant_account().storage_scheme,
            updated_customer,
            merchant_context.get_merchant_key_store(),
            frm_suggestion,
            header_payload.clone(),
        )
        .await?;

    let router_data = if should_continue_further {
        // The status of payment_attempt and intent will be updated in the previous step
        // update this in router_data.
        // This is added because few connector integrations do not update the status,
        // and rely on previous status set in router_data
        // TODO: status is already set when constructing payment data, why should this be done again?
        // router_data.status = payment_data.get_payment_attempt().status;
        router_data
            .decide_flows(
                state,
                &connector,
                call_connector_action,
                connector_request,
                business_profile,
                header_payload.clone(),
                return_raw_connector_response,
            )
            .await
    } else {
        Ok(router_data)
    }?;

    Ok(router_data)
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
#[allow(clippy::type_complexity)]
#[instrument(skip_all)]
pub async fn call_connector_service_prerequisites<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    call_connector_action: CallConnectorAction,
    schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    is_retry_payment: bool,
    should_retry_with_pan: bool,
    all_keys_required: Option<bool>,
) -> RouterResult<(
    domain::MerchantConnectorAccountTypeDetails,
    Option<storage::CustomerUpdate>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
)>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let merchant_connector_account_type_details =
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
            helpers::get_merchant_connector_account_v2(
                state,
                merchant_context.get_merchant_key_store(),
                connector.merchant_connector_id.as_ref(),
            )
            .await?,
        ));

    operation
        .to_domain()?
        .populate_payment_data(
            state,
            payment_data,
            merchant_context,
            business_profile,
            &connector,
        )
        .await?;

    let updated_customer = call_create_connector_customer_if_required(
        state,
        customer,
        merchant_context,
        &merchant_connector_account_type_details,
        payment_data,
    )
    .await?;

    let router_data = payment_data
        .construct_router_data(
            state,
            connector.connector.id(),
            merchant_context,
            customer,
            &merchant_connector_account_type_details,
            None,
            None,
        )
        .await?;

    Ok((
        merchant_connector_account_type_details,
        updated_customer,
        router_data,
    ))
}


pub struct PlacetopayError {
    pub status: PlacetopayErrorStatus,
    pub message: String,
    pub reason: String,
}


pub struct MifinityRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}


pub fn is_key_deleted(&self) -> bool {
        matches!(self, Self::KeyDeleted)
    }


File: crates/router/src/locale.rs

use rust_i18n::i18n;

i18n!("locales", fallback = "en");


/// Returns customer_user_agent_extended with customer_user_agent as fallback
    pub fn get_user_agent_extended(&self) -> Option<String> {
        self.customer_user_agent_extended
            .clone()
            .or_else(|| self.customer_user_agent.clone())
    }


pub fn add_negative_filter_clause(
        &mut self,
        key: impl ToSql<T>,
        value: impl ToSql<T>,
    ) -> QueryResult<()> {
        self.add_custom_filter_clause(key, value, FilterTypes::NotEqual)
    }


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


pub fn get_merchant_id(&self) -> id_type::MerchantId {
        self.merchant_id.clone()
    }


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


pub async fn call_update_gateway_score_open_router(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<api_models::open_router::UpdateScorePayload>,
) -> impl Responder {
    let flow = Flow::DecisionEngineGatewayFeedbackCall;
    Box::pin(oss_api::server_wrap(
        flow,
        state,
        &req,
        payload.clone(),
        |state, _auth, payload, _| {
            routing::update_gateway_score_open_router(state.clone(), payload)
        },
        &auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl Shift4AuthorizePreprocessingCommon for PaymentsAuthorizeData {
    fn get_email_optional(&self) -> Option<pii::Email> {
        self.email.clone()
    }

    fn get_complete_authorize_url(&self) -> Option<String> {
        self.complete_authorize_url.clone()
    }

    fn get_currency_required(
        &self,
    ) -> Result<enums::Currency, error_stack::Report<errors::ConnectorError>> {
        Ok(self.currency)
    }
    fn get_payment_method_data_required(
        &self,
    ) -> Result<PaymentMethodData, error_stack::Report<errors::ConnectorError>> {
        Ok(self.payment_method_data.clone())
    }

    fn is_automatic_capture(&self) -> Result<bool, Error> {
        self.is_auto_capture()
    }

    fn get_router_return_url(&self) -> Option<String> {
        self.router_return_url.clone()
    }

    fn get_metadata(
        &self,
    ) -> Result<Option<serde_json::Value>, error_stack::Report<errors::ConnectorError>> {
        Ok(self.metadata.clone())
    }
}

impl Shift4AuthorizePreprocessingCommon for for PaymentsAuthorizeData


impl api::PaymentToken for Authorizedotnet {}

impl api::PaymentToken for for Authorizedotnet


pub struct ApplePayTokenizedCard {
    transaction_type: TransactionType,
}


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

impl ConnectorValidation for for Inespay


File: crates/connector_configs/src/connector.rs
 Public functions: 5
 Public structs: 9

use std::collections::HashMap;

#[cfg(feature = "payouts")]
use api_models::enums::PayoutConnectors;
use api_models::{
    enums::{AuthenticationConnectors, Connector, PmAuthConnectors, TaxConnectors},
    payments,
};
use serde::{Deserialize, Serialize};
use toml;

use crate::common_config::{CardProvider, InputData, Provider, ZenApplePay};

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct PayloadCurrencyAuthKeyType {
    pub api_key: String,
    pub processing_account_id: String,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct Classic {
    pub password_classic: String,
    pub username_classic: String,
    pub merchant_id_classic: String,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct Evoucher {
    pub password_evoucher: String,
    pub username_evoucher: String,
    pub merchant_id_evoucher: String,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct CashtoCodeCurrencyAuthKeyType {
    pub classic: Classic,
    pub evoucher: Evoucher,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CurrencyAuthValue {
    CashtoCode(CashtoCodeCurrencyAuthKeyType),
    Payload(PayloadCurrencyAuthKeyType),
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub enum ConnectorAuthType {
    HeaderKey {
        api_key: String,
    },
    BodyKey {
        api_key: String,
        key1: String,
    },
    SignatureKey {
        api_key: String,
        key1: String,
        api_secret: String,
    },
    MultiAuthKey {
        api_key: String,
        key1: String,
        api_secret: String,
        key2: String,
    },
    CurrencyAuthKey {
        auth_key_map: HashMap<String, CurrencyAuthValue>,
    },
    CertificateAuth {
        certificate: String,
        private_key: String,
    },
    #[default]
    NoKey,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum ApplePayTomlConfig {
    Standard(Box<payments::ApplePayMetadata>),
    Zen(ZenApplePay),
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum KlarnaEndpoint {
    Europe,
    NorthAmerica,
    Oceania,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ConfigMerchantAdditionalDetails {
    pub open_banking_recipient_data: Option<InputData>,
    pub account_data: Option<InputData>,
    pub iban: Option<Vec<InputData>>,
    pub bacs: Option<Vec<InputData>>,
    pub connector_recipient_id: Option<InputData>,
    pub wallet_id: Option<InputData>,
    pub faster_payments: Option<Vec<InputData>>,
    pub sepa: Option<Vec<InputData>>,
    pub sepa_instant: Option<Vec<InputData>>,
    pub elixir: Option<Vec<InputData>>,
    pub bankgiro: Option<Vec<InputData>>,
    pub plusgiro: Option<Vec<InputData>>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ConfigMetadata {
    pub merchant_config_currency: Option<InputData>,
    pub merchant_account_id: Option<InputData>,
    pub account_name: Option<InputData>,
    pub account_type: Option<InputData>,
    pub terminal_id: Option<InputData>,
    pub google_pay: Option<Vec<InputData>>,
    pub apple_pay: Option<Vec<InputData>>,
    pub merchant_id: Option<InputData>,
    pub endpoint_prefix: Option<InputData>,
    pub mcc: Option<InputData>,
    pub merchant_country_code: Option<InputData>,
    pub merchant_name: Option<InputData>,
    pub acquirer_bin: Option<InputData>,
    pub acquirer_merchant_id: Option<InputData>,
    pub acquirer_country_code: Option<InputData>,
    pub three_ds_requestor_name: Option<InputData>,
    pub three_ds_requestor_id: Option<InputData>,
    pub pull_mechanism_for_external_3ds_enabled: Option<InputData>,
    pub klarna_region: Option<InputData>,
    pub pricing_type: Option<InputData>,
    pub source_balance_account: Option<InputData>,
    pub brand_id: Option<InputData>,
    pub destination_account_number: Option<InputData>,
    pub dpa_id: Option<InputData>,
    pub dpa_name: Option<InputData>,
    pub locale: Option<InputData>,
    pub card_brands: Option<InputData>,
    pub merchant_category_code: Option<InputData>,
    pub merchant_configuration_id: Option<InputData>,
    pub currency_id: Option<InputData>,
    pub platform_id: Option<InputData>,
    pub ledger_account_id: Option<InputData>,
    pub tenant_id: Option<InputData>,
    pub platform_url: Option<InputData>,
    pub report_group: Option<InputData>,
    pub proxy_url: Option<InputData>,
    pub shop_name: Option<InputData>,
    pub merchant_funding_source: Option<InputData>,
    pub account_id: Option<String>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ConnectorWalletDetailsConfig {
    pub samsung_pay: Option<Vec<InputData>>,
    pub paze: Option<Vec<InputData>>,
    pub google_pay: Option<Vec<InputData>>,
    pub amazon_pay: Option<Vec<InputData>>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ConnectorTomlConfig {
    pub connector_auth: Option<ConnectorAuthType>,
    pub connector_webhook_details: Option<api_models::admin::MerchantConnectorWebhookDetails>,
    pub metadata: Option<Box<ConfigMetadata>>,
    pub connector_wallets_details: Option<Box<ConnectorWalletDetailsConfig>>,
    pub additional_merchant_data: Option<Box<ConfigMerchantAdditionalDetails>>,
    pub credit: Option<Vec<CardProvider>>,
    pub debit: Option<Vec<CardProvider>>,
    pub bank_transfer: Option<Vec<Provider>>,
    pub bank_redirect: Option<Vec<Provider>>,
    pub bank_debit: Option<Vec<Provider>>,
    pub open_banking: Option<Vec<Provider>>,
    pub pay_later: Option<Vec<Provider>>,
    pub wallet: Option<Vec<Provider>>,
    pub crypto: Option<Vec<Provider>>,
    pub reward: Option<Vec<Provider>>,
    pub upi: Option<Vec<Provider>>,
    pub voucher: Option<Vec<Provider>>,
    pub gift_card: Option<Vec<Provider>>,
    pub card_redirect: Option<Vec<Provider>>,
    pub is_verifiable: Option<bool>,
    pub real_time_payment: Option<Vec<Provider>>,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ConnectorConfig {
    pub authipay: Option<ConnectorTomlConfig>,
    pub juspaythreedsserver: Option<ConnectorTomlConfig>,
    pub katapult: Option<ConnectorTomlConfig>,
    pub aci: Option<ConnectorTomlConfig>,
    pub adyen: Option<ConnectorTomlConfig>,
    pub affirm: Option<ConnectorTomlConfig>,
    #[cfg(feature = "payouts")]
    pub adyen_payout: Option<ConnectorTomlConfig>,
    #[cfg(feature = "payouts")]
    pub adyenplatform_payout: Option<ConnectorTomlConfig>,
    pub airwallex: Option<ConnectorTomlConfig>,
    pub amazonpay: Option<ConnectorTomlConfig>,
    pub archipel: Option<ConnectorTomlConfig>,
    pub authorizedotnet: Option<ConnectorTomlConfig>,
    pub bamboraapac: Option<ConnectorTomlConfig>,
    pub bankofamerica: Option<ConnectorTomlConfig>,
    pub barclaycard: Option<ConnectorTomlConfig>,
    pub billwerk: Option<ConnectorTomlConfig>,
    pub bitpay: Option<ConnectorTomlConfig>,
    pub blackhawknetwork: Option<ConnectorTomlConfig>,
    pub bluecode: Option<ConnectorTomlConfig>,
    pub bluesnap: Option<ConnectorTomlConfig>,
    pub boku: Option<ConnectorTomlConfig>,
    pub braintree: Option<ConnectorTomlConfig>,
    pub breadpay: Option<ConnectorTomlConfig>,
    pub cashtocode: Option<ConnectorTomlConfig>,
    pub celero: Option<ConnectorTomlConfig>,
    pub chargebee: Option<ConnectorTomlConfig>,
    pub custombilling: Option<ConnectorTomlConfig>,
    pub checkbook: Option<ConnectorTomlConfig>,
    pub checkout: Option<ConnectorTomlConfig>,
    pub coinbase: Option<ConnectorTomlConfig>,
    pub coingate: Option<ConnectorTomlConfig>,
    pub cryptopay: Option<ConnectorTomlConfig>,
    pub ctp_visa: Option<ConnectorTomlConfig>,
    pub cybersource: Option<ConnectorTomlConfig>,
    #[cfg(feature = "payouts")]
    pub cybersource_payout: Option<ConnectorTomlConfig>,
    pub iatapay: Option<ConnectorTomlConfig>,
    pub itaubank: Option<ConnectorTomlConfig>,
    pub opennode: Option<ConnectorTomlConfig>,
    pub bambora: Option<ConnectorTomlConfig>,
    pub datatrans: Option<ConnectorTomlConfig>,
    pub deutschebank: Option<ConnectorTomlConfig>,
    pub digitalvirgo: Option<ConnectorTomlConfig>,
    pub dlocal: Option<ConnectorTomlConfig>,
    pub dwolla: Option<ConnectorTomlConfig>,
    pub ebanx_payout: Option<ConnectorTomlConfig>,
    pub elavon: Option<ConnectorTomlConfig>,
    pub facilitapay: Option<ConnectorTomlConfig>,
    pub fiserv: Option<ConnectorTomlConfig>,
    pub fiservemea: Option<ConnectorTomlConfig>,
    pub fiuu: Option<ConnectorTomlConfig>,
    pub flexiti: Option<ConnectorTomlConfig>,
    pub forte: Option<ConnectorTomlConfig>,
    pub getnet: Option<ConnectorTomlConfig>,
    pub globalpay: Option<ConnectorTomlConfig>,
    pub globepay: Option<ConnectorTomlConfig>,
    pub gocardless: Option<ConnectorTomlConfig>,
    pub gpayments: Option<ConnectorTomlConfig>,
    pub hipay: Option<ConnectorTomlConfig>,
    pub helcim: Option<ConnectorTomlConfig>,
    pub hyperswitch_vault: Option<ConnectorTomlConfig>,
    pub hyperwallet: Option<ConnectorTomlConfig>,
    pub inespay: Option<ConnectorTomlConfig>,
    pub jpmorgan: Option<ConnectorTomlConfig>,
    pub klarna: Option<ConnectorTomlConfig>,
    pub mifinity: Option<ConnectorTomlConfig>,
    pub mollie: Option<ConnectorTomlConfig>,
    pub moneris: Option<ConnectorTomlConfig>,
    pub mpgs: Option<ConnectorTomlConfig>,
    pub multisafepay: Option<ConnectorTomlConfig>,
    pub nexinets: Option<ConnectorTomlConfig>,
    pub nexixpay: Option<ConnectorTomlConfig>,
    pub nmi: Option<ConnectorTomlConfig>,
    pub nomupay_payout: Option<ConnectorTomlConfig>,
    pub noon: Option<ConnectorTomlConfig>,
    pub nordea: Option<ConnectorTomlConfig>,
    pub novalnet: Option<ConnectorTomlConfig>,
    pub nuvei: Option<ConnectorTomlConfig>,
    pub paybox: Option<ConnectorTomlConfig>,
    pub payload: Option<ConnectorTomlConfig>,
    pub payme: Option<ConnectorTomlConfig>,
    #[cfg(feature = "payouts")]
    pub payone_payout: Option<ConnectorTomlConfig>,
    pub paypal: Option<ConnectorTomlConfig>,
    pub paysafe: Option<ConnectorTomlConfig>,
    #[cfg(feature = "payouts")]
    pub paypal_payout: Option<ConnectorTomlConfig>,
    pub paystack: Option<ConnectorTomlConfig>,
    pub paytm: Option<ConnectorTomlConfig>,
    pub payu: Option<ConnectorTomlConfig>,
    pub phonepe: Option<ConnectorTomlConfig>,
    pub placetopay: Option<ConnectorTomlConfig>,
    pub plaid: Option<ConnectorTomlConfig>,
    pub powertranz: Option<ConnectorTomlConfig>,
    pub prophetpay: Option<ConnectorTomlConfig>,
    pub razorpay: Option<ConnectorTomlConfig>,
    pub recurly: Option<ConnectorTomlConfig>,
    pub riskified: Option<ConnectorTomlConfig>,
    pub rapyd: Option<ConnectorTomlConfig>,
    pub redsys: Option<ConnectorTomlConfig>,
    pub santander: Option<ConnectorTomlConfig>,
    pub shift4: Option<ConnectorTomlConfig>,
    pub sift: Option<ConnectorTomlConfig>,
    pub silverflow: Option<ConnectorTomlConfig>,
    pub stripe: Option<ConnectorTomlConfig>,
    #[cfg(feature = "payouts")]
    pub stripe_payout: Option<ConnectorTomlConfig>,
    pub stripebilling: Option<ConnectorTomlConfig>,
    pub signifyd: Option<ConnectorTomlConfig>,
    pub tokenio: Option<ConnectorTomlConfig>,
    pub trustpay: Option<ConnectorTomlConfig>,
    pub trustpayments: Option<ConnectorTomlConfig>,
    pub threedsecureio: Option<ConnectorTomlConfig>,
    pub netcetera: Option<ConnectorTomlConfig>,
    pub tsys: Option<ConnectorTomlConfig>,
    pub vgs: Option<ConnectorTomlConfig>,
    pub volt: Option<ConnectorTomlConfig>,
    pub wellsfargo: Option<ConnectorTomlConfig>,
    #[cfg(feature = "payouts")]
    pub wise_payout: Option<ConnectorTomlConfig>,
    pub worldline: Option<ConnectorTomlConfig>,
    pub worldpay: Option<ConnectorTomlConfig>,
    pub worldpayvantiv: Option<ConnectorTomlConfig>,
    pub worldpayxml: Option<ConnectorTomlConfig>,
    pub xendit: Option<ConnectorTomlConfig>,
    pub square: Option<ConnectorTomlConfig>,
    pub stax: Option<ConnectorTomlConfig>,
    pub dummy_connector: Option<ConnectorTomlConfig>,
    pub stripe_test: Option<ConnectorTomlConfig>,
    pub paypal_test: Option<ConnectorTomlConfig>,
    pub zen: Option<ConnectorTomlConfig>,
    pub zsl: Option<ConnectorTomlConfig>,
    pub taxjar: Option<ConnectorTomlConfig>,
    pub ctp_mastercard: Option<ConnectorTomlConfig>,
    pub unified_authentication_service: Option<ConnectorTomlConfig>,
}

impl ConnectorConfig {
    fn new() -> Result<Self, String> {
        let config_str = if cfg!(feature = "production") {
            include_str!("../toml/production.toml")
        } else if cfg!(feature = "sandbox") {
            include_str!("../toml/sandbox.toml")
        } else {
            include_str!("../toml/development.toml")
        };
        let config = toml::from_str::<Self>(config_str);
        match config {
            Ok(data) => Ok(data),
            Err(err) => Err(err.to_string()),
        }
    }

    #[cfg(feature = "payouts")]
    pub fn get_payout_connector_config(
        connector: PayoutConnectors,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            PayoutConnectors::Adyen => Ok(connector_data.adyen_payout),
            PayoutConnectors::Adyenplatform => Ok(connector_data.adyenplatform_payout),
            PayoutConnectors::Cybersource => Ok(connector_data.cybersource_payout),
            PayoutConnectors::Ebanx => Ok(connector_data.ebanx_payout),
            PayoutConnectors::Nomupay => Ok(connector_data.nomupay_payout),
            PayoutConnectors::Payone => Ok(connector_data.payone_payout),
            PayoutConnectors::Paypal => Ok(connector_data.paypal_payout),
            PayoutConnectors::Stripe => Ok(connector_data.stripe_payout),
            PayoutConnectors::Wise => Ok(connector_data.wise_payout),
        }
    }

    pub fn get_authentication_connector_config(
        connector: AuthenticationConnectors,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            AuthenticationConnectors::Threedsecureio => Ok(connector_data.threedsecureio),
            AuthenticationConnectors::Netcetera => Ok(connector_data.netcetera),
            AuthenticationConnectors::Gpayments => Ok(connector_data.gpayments),
            AuthenticationConnectors::CtpMastercard => Ok(connector_data.ctp_mastercard),
            AuthenticationConnectors::CtpVisa => Ok(connector_data.ctp_visa),
            AuthenticationConnectors::UnifiedAuthenticationService => {
                Ok(connector_data.unified_authentication_service)
            }
            AuthenticationConnectors::Juspaythreedsserver => Ok(connector_data.juspaythreedsserver),
        }
    }

    pub fn get_tax_processor_config(
        connector: TaxConnectors,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            TaxConnectors::Taxjar => Ok(connector_data.taxjar),
        }
    }

    pub fn get_pm_authentication_processor_config(
        connector: PmAuthConnectors,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            PmAuthConnectors::Plaid => Ok(connector_data.plaid),
        }
    }

    pub fn get_connector_config(
        connector: Connector,
    ) -> Result<Option<ConnectorTomlConfig>, String> {
        let connector_data = Self::new()?;
        match connector {
            Connector::Aci => Ok(connector_data.aci),
            Connector::Authipay => Ok(connector_data.authipay),
            Connector::Adyen => Ok(connector_data.adyen),
            Connector::Affirm => Ok(connector_data.affirm),
            Connector::Adyenplatform => Err("Use get_payout_connector_config".to_string()),
            Connector::Airwallex => Ok(connector_data.airwallex),
            Connector::Amazonpay => Ok(connector_data.amazonpay),
            Connector::Archipel => Ok(connector_data.archipel),
            Connector::Authorizedotnet => Ok(connector_data.authorizedotnet),
            Connector::Bamboraapac => Ok(connector_data.bamboraapac),
            Connector::Bankofamerica => Ok(connector_data.bankofamerica),
            Connector::Barclaycard => Ok(connector_data.barclaycard),
            Connector::Billwerk => Ok(connector_data.billwerk),
            Connector::Bitpay => Ok(connector_data.bitpay),
            Connector::Bluesnap => Ok(connector_data.bluesnap),
            Connector::Bluecode => Ok(connector_data.bluecode),
            Connector::Blackhawknetwork => Ok(connector_data.blackhawknetwork),
            Connector::Boku => Ok(connector_data.boku),
            Connector::Braintree => Ok(connector_data.braintree),
            Connector::Breadpay => Ok(connector_data.breadpay),
            Connector::Cashtocode => Ok(connector_data.cashtocode),
            Connector::Celero => Ok(connector_data.celero),
            Connector::Chargebee => Ok(connector_data.chargebee),
            Connector::Checkbook => Ok(connector_data.checkbook),
            Connector::Checkout => Ok(connector_data.checkout),
            Connector::Coinbase => Ok(connector_data.coinbase),
            Connector::Coingate => Ok(connector_data.coingate),
            Connector::Cryptopay => Ok(connector_data.cryptopay),
            Connector::CtpVisa => Ok(connector_data.ctp_visa),
            Connector::Custombilling => Ok(connector_data.custombilling),
            Connector::Cybersource => Ok(connector_data.cybersource),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyBillingConnector => Ok(connector_data.dummy_connector),
            Connector::Iatapay => Ok(connector_data.iatapay),
            Connector::Itaubank => Ok(connector_data.itaubank),
            Connector::Opennode => Ok(connector_data.opennode),
            Connector::Bambora => Ok(connector_data.bambora),
            Connector::Datatrans => Ok(connector_data.datatrans),
            Connector::Deutschebank => Ok(connector_data.deutschebank),
            Connector::Digitalvirgo => Ok(connector_data.digitalvirgo),
            Connector::Dlocal => Ok(connector_data.dlocal),
            Connector::Dwolla => Ok(connector_data.dwolla),
            Connector::Ebanx => Ok(connector_data.ebanx_payout),
            Connector::Elavon => Ok(connector_data.elavon),
            Connector::Facilitapay => Ok(connector_data.facilitapay),
            Connector::Fiserv => Ok(connector_data.fiserv),
            Connector::Fiservemea => Ok(connector_data.fiservemea),
            Connector::Fiuu => Ok(connector_data.fiuu),
            Connector::Flexiti => Ok(connector_data.flexiti),
            Connector::Forte => Ok(connector_data.forte),
            Connector::Getnet => Ok(connector_data.getnet),
            Connector::Globalpay => Ok(connector_data.globalpay),
            Connector::Globepay => Ok(connector_data.globepay),
            Connector::Gocardless => Ok(connector_data.gocardless),
            Connector::Gpayments => Ok(connector_data.gpayments),
            Connector::Hipay => Ok(connector_data.hipay),
            Connector::HyperswitchVault => Ok(connector_data.hyperswitch_vault),
            Connector::Helcim => Ok(connector_data.helcim),
            Connector::Inespay => Ok(connector_data.inespay),
            Connector::Jpmorgan => Ok(connector_data.jpmorgan),
            Connector::Juspaythreedsserver => Ok(connector_data.juspaythreedsserver),
            Connector::Klarna => Ok(connector_data.klarna),
            Connector::Mifinity => Ok(connector_data.mifinity),
            Connector::Mollie => Ok(connector_data.mollie),
            Connector::Moneris => Ok(connector_data.moneris),
            Connector::Multisafepay => Ok(connector_data.multisafepay),
            Connector::Nexinets => Ok(connector_data.nexinets),
            Connector::Nexixpay => Ok(connector_data.nexixpay),
            Connector::Prophetpay => Ok(connector_data.prophetpay),
            Connector::Nmi => Ok(connector_data.nmi),
            Connector::Nordea => Ok(connector_data.nordea),
            Connector::Nomupay => Err("Use get_payout_connector_config".to_string()),
            Connector::Novalnet => Ok(connector_data.novalnet),
            Connector::Noon => Ok(connector_data.noon),
            Connector::Nuvei => Ok(connector_data.nuvei),
            Connector::Paybox => Ok(connector_data.paybox),
            Connector::Payload => Ok(connector_data.payload),
            Connector::Payme => Ok(connector_data.payme),
            Connector::Payone => Err("Use get_payout_connector_config".to_string()),
            Connector::Paypal => Ok(connector_data.paypal),
            Connector::Paysafe => Ok(connector_data.paysafe),
            Connector::Paystack => Ok(connector_data.paystack),
            Connector::Payu => Ok(connector_data.payu),
            Connector::Placetopay => Ok(connector_data.placetopay),
            Connector::Plaid => Ok(connector_data.plaid),
            Connector::Powertranz => Ok(connector_data.powertranz),
            Connector::Razorpay => Ok(connector_data.razorpay),
            Connector::Rapyd => Ok(connector_data.rapyd),
            Connector::Recurly => Ok(connector_data.recurly),
            Connector::Redsys => Ok(connector_data.redsys),
            Connector::Riskified => Ok(connector_data.riskified),
            Connector::Santander => Ok(connector_data.santander),
            Connector::Shift4 => Ok(connector_data.shift4),
            Connector::Signifyd => Ok(connector_data.signifyd),
            Connector::Silverflow => Ok(connector_data.silverflow),
            Connector::Square => Ok(connector_data.square),
            Connector::Stax => Ok(connector_data.stax),
            Connector::Stripe => Ok(connector_data.stripe),
            Connector::Stripebilling => Ok(connector_data.stripebilling),
            Connector::Tokenio => Ok(connector_data.tokenio),
            Connector::Trustpay => Ok(connector_data.trustpay),
            Connector::Threedsecureio => Ok(connector_data.threedsecureio),
            Connector::Taxjar => Ok(connector_data.taxjar),
            Connector::Tsys => Ok(connector_data.tsys),
            Connector::Vgs => Ok(connector_data.vgs),
            Connector::Volt => Ok(connector_data.volt),
            Connector::Wellsfargo => Ok(connector_data.wellsfargo),
            Connector::Wise => Err("Use get_payout_connector_config".to_string()),
            Connector::Worldline => Ok(connector_data.worldline),
            Connector::Worldpay => Ok(connector_data.worldpay),
            Connector::Worldpayvantiv => Ok(connector_data.worldpayvantiv),
            Connector::Worldpayxml => Ok(connector_data.worldpayxml),
            Connector::Zen => Ok(connector_data.zen),
            Connector::Zsl => Ok(connector_data.zsl),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector1 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector2 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector3 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector4 => Ok(connector_data.stripe_test),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector5 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector6 => Ok(connector_data.dummy_connector),
            #[cfg(feature = "dummy_connector")]
            Connector::DummyConnector7 => Ok(connector_data.paypal_test),
            Connector::Netcetera => Ok(connector_data.netcetera),
            Connector::CtpMastercard => Ok(connector_data.ctp_mastercard),
            Connector::Xendit => Ok(connector_data.xendit),
            Connector::Paytm => Ok(connector_data.paytm),
            Connector::Phonepe => Ok(connector_data.phonepe),
        }
    }
}


pub struct NmiCompleteResponse {
    pub response: Response,
    pub responsetext: String,
    pub authcode: Option<String>,
    pub transactionid: String,
    pub avsresponse: Option<String>,
    pub cvvresponse: Option<String>,
    pub orderid: String,
    pub response_code: String,
    customer_vault_id: Option<Secret<String>>,
}


impl api::PaymentAuthorize for Facilitapay {}

impl api::PaymentAuthorize for for Facilitapay


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

mod transformers;

use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{
        PaymentsAuthorizeType, PaymentsCaptureType, PaymentsSyncType, RefundExecuteType,
        RefundSyncType, Response,
    },
    webhooks,
};
use masking::{ExposeInterface, Mask};
use transformers as opayo;

use crate::{constants::headers, types::ResponseRouterData, utils::convert_amount};

#[derive(Clone)]
pub struct Opayo {
    amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
}

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

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

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Opayo
{
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Opayo
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let content_type = ConnectorCommon::common_get_content_type(self);
        let mut common_headers = self.get_auth_header(&req.connector_auth_type)?;
        common_headers.push((
            headers::CONTENT_TYPE.to_string(),
            content_type.to_string().into(),
        ));
        Ok(common_headers)
    }
}

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

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = opayo::OpayoAuthType::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: opayo::OpayoErrorResponse =
            res.response
                .parse_struct("OpayoErrorResponse")
                .change_context(errors::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: 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,
        })
    }
}

impl ConnectorValidation for Opayo {
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<enums::CaptureMethod>,
        _payment_method: enums::PaymentMethod,
        _pmt: Option<enums::PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();
        match capture_method {
            enums::CaptureMethod::Automatic
            | enums::CaptureMethod::Manual
            | enums::CaptureMethod::SequentialAutomatic => Ok(()),
            enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => Err(
                crate::utils::construct_not_supported_error_report(capture_method, self.id()),
            ),
        }
    }
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Opayo {
    //TODO: implement sessions flow
}

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

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Opayo {
    fn build_request(
        &self,
        _req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(
            errors::ConnectorError::NotImplemented("Setup Mandate flow for Opayo".to_string())
                .into(),
        )
    }
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Opayo {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;
        let connector_router_data = opayo::OpayoRouterData::from((amount, req));
        let connector_req = opayo::OpayoPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsAuthorizeType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(PaymentsAuthorizeType::get_headers(self, req, connectors)?)
                .set_body(PaymentsAuthorizeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: opayo::OpayoPaymentsResponse = res
            .response
            .parse_struct("Opayo PaymentsAuthorizeResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Opayo {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(PaymentsSyncType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: opayo::OpayoPaymentsResponse = res
            .response
            .parse_struct("opayo PaymentsSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Opayo {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsCaptureType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(PaymentsCaptureType::get_headers(self, req, connectors)?)
                .set_body(PaymentsCaptureType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
        let response: opayo::OpayoPaymentsResponse = res
            .response
            .parse_struct("Opayo PaymentsCaptureResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

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

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Opayo {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let refund_amount = convert_amount(
            self.amount_converter,
            req.request.minor_refund_amount,
            req.request.currency,
        )?;
        let connector_router_data = opayo::OpayoRouterData::from((refund_amount, req));
        let connector_req = opayo::OpayoRefundRequest::try_from(&connector_router_data)?;

        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let request = RequestBuilder::new()
            .method(Method::Post)
            .url(&RefundExecuteType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(RefundExecuteType::get_headers(self, req, connectors)?)
            .set_body(RefundExecuteType::get_request_body(self, req, connectors)?)
            .build();
        Ok(Some(request))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: opayo::RefundResponse = res
            .response
            .parse_struct("opayo RefundResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Opayo {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &RefundSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&RefundSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(RefundSyncType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: opayo::RefundResponse = res
            .response
            .parse_struct("opayo RefundSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

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

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

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

impl ConnectorSpecifications for Opayo {}


OpenAPI Block Path: components.schemas.BacsBankTransferAdditionalData
{
  "type": "object",
  "description": "Masked payout method details for bacs bank transfer payout method",
  "required": [
    "bank_sort_code",
    "bank_account_number"
  ],
  "properties": {
    "bank_sort_code": {
      "type": "string",
      "description": "Partially masked sort code for Bacs payment method",
      "example": "108800"
    },
    "bank_account_number": {
      "type": "string",
      "description": "Bank account's owner name",
      "example": "0001****3456"
    },
    "bank_name": {
      "type": "string",
      "description": "Bank name",
      "example": "Deutsche Bank",
      "nullable": true
    },
    "bank_country_code": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CountryAlpha2"
        }
      ],
      "nullable": true
    },
    "bank_city": {
      "type": "string",
      "description": "Bank city",
      "example": "California",
      "nullable": true
    }
  }
}


OpenAPI Block Path: components.schemas.ListBlocklistQuery
{
  "type": "object",
  "required": [
    "data_kind"
  ],
  "properties": {
    "data_kind": {
      "$ref": "#/components/schemas/BlocklistDataKind"
    },
    "limit": {
      "type": "integer",
      "format": "int32",
      "minimum": 0
    },
    "offset": {
      "type": "integer",
      "format": "int32",
      "minimum": 0
    }
  }
}


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


pub async fn add_api_key(
    state: &SessionState,
    api_key: Secret<String>,
    merchant_id: common_utils::id_type::MerchantId,
    key_id: common_utils::id_type::ApiKeyId,
    expiry: Option<u64>,
) -> CustomResult<(), ApiClientError> {
    let decision_config = if let Some(config) = &state.conf.decision {
        config
    } else {
        return Ok(());
    };

    let rule = RuleRequest {
        tag: state.tenant.schema.clone(),
        expiry,
        variant: AuthRuleType::ApiKey {
            api_key,
            identifiers: Identifiers::ApiKey {
                merchant_id,
                key_id,
            },
        },
    };

    call_decision_service(state, decision_config, rule, RULE_ADD_METHOD).await
}


pub struct BillingInformation {
    first_name: Secret<String>,
    last_name: Secret<String>,
    address_1: Secret<String>,
    address_2: Secret<String>,
    city: Secret<String>,
    postal_code: Secret<String>,
    province: Secret<String>,
}


pub trait VerifyConnector {
    async fn verify(
        state: &SessionState,
        connector_data: VerifyConnectorData,
    ) -> errors::RouterResponse<()> {
        let authorize_data = connector_data.get_payment_authorize_data();
        let access_token = Self::get_access_token(state, connector_data.clone()).await?;
        let router_data = connector_data.get_router_data(state, authorize_data, access_token);

        let request = connector_data
            .connector
            .get_connector_integration()
            .build_request(&router_data, &state.conf.connectors)
            .change_context(errors::ApiErrorResponse::InvalidRequestData {
                message: "Payment request cannot be built".to_string(),
            })?
            .ok_or(errors::ApiErrorResponse::InternalServerError)?;

        let response =
            services::call_connector_api(&state.to_owned(), request, "verify_connector_request")
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)?;

        match response {
            Ok(_) => Ok(services::ApplicationResponse::StatusOk),
            Err(error_response) => {
                Self::handle_payment_error_response::<
                    api::Authorize,
                    types::PaymentFlowData,
                    types::PaymentsAuthorizeData,
                    types::PaymentsResponseData,
                >(
                    connector_data.connector.get_connector_integration(),
                    error_response,
                )
                .await
            }
        }
    }

    async fn get_access_token(
        _state: &SessionState,
        _connector_data: VerifyConnectorData,
    ) -> errors::CustomResult<Option<types::AccessToken>, errors::ApiErrorResponse> {
        // AccessToken is None for the connectors without the AccessToken Flow.
        // If a connector has that, then it should override this implementation.
        Ok(None)
    }

    async fn handle_payment_error_response<F, ResourceCommonData, Req, Resp>(
        // connector: &(dyn api::Connector + Sync),
        connector: BoxedConnectorIntegrationInterface<F, ResourceCommonData, Req, Resp>,
        error_response: types::Response,
    ) -> errors::RouterResponse<()> {
        let error = connector
            .get_error_response(error_response, None)
            .change_context(errors::ApiErrorResponse::InternalServerError)?;
        Err(errors::ApiErrorResponse::InvalidRequestData {
            message: error.reason.unwrap_or(error.message),
        }
        .into())
    }

    async fn handle_access_token_error_response<F, ResourceCommonData, Req, Resp>(
        connector: BoxedConnectorIntegrationInterface<F, ResourceCommonData, Req, Resp>,
        error_response: types::Response,
    ) -> errors::RouterResult<Option<types::AccessToken>> {
        let error = connector
            .get_error_response(error_response, None)
            .change_context(errors::ApiErrorResponse::InternalServerError)?;
        Err(errors::ApiErrorResponse::InvalidRequestData {
            message: error.reason.unwrap_or(error.message),
        }
        .into())
    }
}


pub struct HyperwalletCard {
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
    complete: bool,
}


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


/// Default constructor.
    pub fn new() -> Result<Self, config::ConfigError> {
        Self::new_with_config_path(None)
    }


pub async fn terminate_auth_select(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<user_api::AuthSelectRequest>,
) -> HttpResponse {
    let flow = Flow::AuthSelect;

    Box::pin(api::server_wrap(
        flow,
        state.clone(),
        &req,
        json_payload.into_inner(),
        |state, user, req, _| user_core::terminate_auth_select(state, user, req),
        &auth::SinglePurposeJWTAuth(TokenPurpose::AuthSelect),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub fn calculate_signature(
    signature_data: String,
) -> Result<Secret<String>, Report<errors::ConnectorError>> {
    let message = signature_data.as_bytes();
    let encoded_data = hex::encode(
        crypto::Md5
            .generate_digest(message)
            .change_context(errors::ConnectorError::RequestEncodingFailed)?,
    );
    Ok(Secret::new(encoded_data))
}