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

pub mod transformers;

use std::sync::LazyLock;

use api_models::{enums, payments::PaymentIdType};
use common_utils::{
    crypto,
    errors::CustomResult,
    ext_traits::{ByteSliceExt, BytesExt},
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector},
};
use error_stack::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::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use masking::{ExposeInterface, Mask};
use transformers as checkbook;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

impl ConnectorValidation for Checkbook {}

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

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

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

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Checkbook {
    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> {
        Ok(format!("{}/v3/invoice", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = self
            .amount_converter
            .convert(req.request.minor_amount, req.request.currency)
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;
        let connector_req = checkbook::CheckbookPaymentsRequest::try_from((amount, req))?;
        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(&types::PaymentsAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::PaymentsAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::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: checkbook::CheckbookPaymentsResponse = res
            .response
            .parse_struct("Checkbook 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 Checkbook {
    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> {
        let connector_txn_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
        Ok(format!(
            "{}/v3/invoice/{}",
            self.base_url(connectors),
            connector_txn_id
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::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: checkbook::CheckbookPaymentsResponse = res
            .response
            .parse_struct("checkbook 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 Checkbook {}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Checkbook {
    fn get_headers(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}v3/invoice/{}",
            self.base_url(connectors),
            req.request.connector_transaction_id
        ))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsCancelRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCancelRouterData, errors::ConnectorError> {
        let response: checkbook::CheckbookPaymentsResponse = res
            .response
            .parse_struct("Checkbook PaymentsCancelResponse")
            .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<Execute, RefundsData, RefundsResponseData> for Checkbook {
    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::NotSupported {
            message: "Refunds are not supported".to_string(),
            connector: "checkbook",
        }
        .into())
    }

    fn get_request_body(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Err(errors::ConnectorError::NotSupported {
            message: "Refunds are not supported".to_string(),
            connector: "checkbook",
        }
        .into())
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let request = RequestBuilder::new()
            .method(Method::Post)
            .url(&types::RefundExecuteType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(types::RefundExecuteType::get_headers(
                self, req, connectors,
            )?)
            .set_body(types::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> {
        Err(errors::ConnectorError::NotImplemented("Refunds are not supported".to_string()).into())
    }

    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 Checkbook {
    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(&types::RefundSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::RefundSyncType::get_headers(self, req, connectors)?)
                .set_body(types::RefundSyncType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        _data: &RefundSyncRouterData,
        _event_builder: Option<&mut ConnectorEvent>,
        _res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("Refunds are not supported".to_string()).into())
    }

    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 Checkbook {
    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let details: checkbook::CheckbookPaymentsResponse = request
            .body
            .parse_struct("CheckbookWebhookResponse")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
            PaymentIdType::ConnectorTransactionId(details.id),
        ))
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let details: checkbook::CheckbookPaymentsResponse = request
            .body
            .parse_struct("CheckbookWebhookResponse")
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
        Ok(api_models::webhooks::IncomingWebhookEvent::from(
            details.status,
        ))
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let details: checkbook::CheckbookPaymentsResponse = request
            .body
            .parse_struct("CheckbookWebhookResponse")
            .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
        Ok(Box::new(details))
    }

    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> {
        Ok(Box::new(crypto::HmacSha256))
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let header_value = request
            .headers
            .get("signature")
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)
            .attach_printable("Failed to get signature for checkbook")?
            .to_str()
            .map_err(|_| errors::ConnectorError::WebhookSignatureNotFound)
            .attach_printable("Failed to get signature for checkbook")?;
        let signature = header_value
            .split(',')
            .find_map(|s| s.strip_prefix("signature="))
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)?;
        hex::decode(signature)
            .change_context(errors::ConnectorError::WebhookSignatureNotFound)
            .attach_printable("Failed to decrypt checkbook webhook payload for verification")
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let header_value = request
            .headers
            .get("signature")
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)?
            .to_str()
            .map_err(|_| errors::ConnectorError::WebhookSignatureNotFound)?;
        let nonce = header_value
            .split(',')
            .find_map(|s| s.strip_prefix("nonce="))
            .ok_or(errors::ConnectorError::WebhookSignatureNotFound)?;
        let message = format!("{}{}", String::from_utf8_lossy(request.body), nonce);
        Ok(message.into_bytes())
    }
}

static CHECKBOOK_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(|| {
        let supported_capture_methods = vec![enums::CaptureMethod::Automatic];

        let mut checkbook_supported_payment_methods = SupportedPaymentMethods::new();

        checkbook_supported_payment_methods.add(
            enums::PaymentMethod::BankTransfer,
            enums::PaymentMethodType::Ach,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::NotSupported,
                supported_capture_methods,
                specific_features: None,
            },
        );
        checkbook_supported_payment_methods
    });

static CHECKBOOK_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Checkbook",
    description:
        "Checkbook is a payment platform that allows users to send and receive digital checks.",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: common_enums::ConnectorIntegrationStatus::Beta,
};

static CHECKBOOK_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 1] = [enums::EventClass::Payments];

impl ConnectorSpecifications for Checkbook {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&CHECKBOOK_CONNECTOR_INFO)
    }

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


pub async fn get_profile_api_events(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Query<api_models::analytics::api_event::ApiLogsRequest>,
    ) -> impl Responder {
        let flow = AnalyticsFlow::GetApiEvents;
        Box::pin(api::server_wrap(
            flow,
            state,
            &req,
            json_payload.into_inner(),
            |state, auth: AuthenticationData, req, _| async move {
                let payment_id = match req.query_param.clone() {
                    QueryType::Payment { payment_id } => payment_id,
                    QueryType::Refund { payment_id, .. } => payment_id,
                    QueryType::Dispute { payment_id, .. } => payment_id,
                };
                utils::check_if_profile_id_is_present_in_payment_intent(payment_id, &state, &auth)
                    .await
                    .change_context(AnalyticsError::AccessForbiddenError)?;
                api_events_core(&state.pool, req, auth.merchant_account.get_id())
                    .await
                    .map(ApplicationResponse::Json)
            },
            &auth::JWTAuth {
                permission: Permission::ProfileAnalyticsRead,
            },
            api_locking::LockAction::NotApplicable,
        ))
        .await
    }


pub struct Classic {
    pub password_classic: String,
    pub username_classic: String,
    pub merchant_id_classic: String,
}


pub struct PaymentToken {
    reusable: Option<bool>,
    decline_duplicates: Option<bool>,
    brand: Option<String>,
    #[serde(rename = "type")]
    token_type: Option<String>,
}


pub async fn get_role_from_token_with_groups(
    state: SessionState,
    user_from_token: UserFromToken,
) -> UserResponse<Vec<PermissionGroup>> {
    let role_info = user_from_token
        .get_role_info_from_db(&state)
        .await
        .attach_printable("Invalid role_id in JWT")?;

    let permissions = role_info.get_permission_groups().to_vec();

    Ok(ApplicationResponse::Json(permissions))
}


File: crates/drainer/src/logger.rs

#[doc(inline)]
pub use router_env::{debug, error, info, warn};


pub async fn record_attempt_core(
    state: SessionState,
    req_state: ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    req: api_models::payments::PaymentsAttemptRecordRequest,
    payment_id: id_type::GlobalPaymentId,
    header_payload: HeaderPayload,
) -> RouterResponse<api_models::payments::PaymentAttemptRecordResponse> {
    tracing::Span::current().record(
        "merchant_id",
        merchant_context
            .get_merchant_account()
            .get_id()
            .get_string_repr(),
    );

    let operation: &operations::payment_attempt_record::PaymentAttemptRecord =
        &operations::payment_attempt_record::PaymentAttemptRecord;
    let boxed_operation: BoxedOperation<
        '_,
        api::RecordAttempt,
        api_models::payments::PaymentsAttemptRecordRequest,
        domain_payments::PaymentAttemptRecordData<api::RecordAttempt>,
    > = Box::new(operation);

    let _validate_result = boxed_operation
        .to_validate_request()?
        .validate_request(&req, &merchant_context)?;

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

    let operations::GetTrackerResponse { payment_data } = boxed_operation
        .to_get_tracker()?
        .get_trackers(
            &state,
            &payment_id,
            &req,
            &merchant_context,
            &profile,
            &header_payload,
        )
        .await?;
    let default_payment_status_data = PaymentStatusData {
        flow: PhantomData,
        payment_intent: payment_data.payment_intent.clone(),
        payment_attempt: payment_data.payment_attempt.clone(),
        attempts: None,
        should_sync_with_connector: false,
        payment_address: payment_data.payment_address.clone(),
        merchant_connector_details: None,
    };

    let payment_status_data = (req.triggered_by == common_enums::TriggeredBy::Internal)
    .then(|| default_payment_status_data.clone())
    .async_unwrap_or_else(|| async {
        match Box::pin(proxy_for_payments_operation_core::<
            api::PSync,
            _,
            _,
            _,
            PaymentStatusData<api::PSync>,
        >(
            &state,
            req_state.clone(),
            merchant_context.clone(),
            profile.clone(),
            operations::PaymentGet,
            api::PaymentsRetrieveRequest {
                force_sync: true,
                expand_attempts: false,
                param: None,
                return_raw_connector_response: None,
                merchant_connector_details: None,
            },
            operations::GetTrackerResponse {
                payment_data: PaymentStatusData {
                    flow: PhantomData,
                    payment_intent: payment_data.payment_intent.clone(),
                    payment_attempt: payment_data.payment_attempt.clone(),
                    attempts: None,
                    should_sync_with_connector: true,
                    payment_address: payment_data.payment_address.clone(),
                    merchant_connector_details: None,
                },
            },
            CallConnectorAction::Trigger,
            HeaderPayload::default(),
            None,
        ))
        .await
        {
            Ok((data, _, _, _)) => data,
            Err(err) => {
                router_env::logger::error!(error=?err, "proxy_for_payments_operation_core failed for external payment attempt");
                default_payment_status_data
            }
        }
    })
    .await;

    let record_payment_data = domain_payments::PaymentAttemptRecordData {
        flow: PhantomData,
        payment_intent: payment_status_data.payment_intent,
        payment_attempt: payment_status_data.payment_attempt,
        revenue_recovery_data: payment_data.revenue_recovery_data.clone(),
        payment_address: payment_data.payment_address.clone(),
    };

    let (_operation, final_payment_data) = boxed_operation
        .to_update_tracker()?
        .update_trackers(
            &state,
            req_state,
            record_payment_data,
            None,
            merchant_context.get_merchant_account().storage_scheme,
            None,
            merchant_context.get_merchant_key_store(),
            None,
            header_payload.clone(),
        )
        .await?;

    transformers::GenerateResponse::generate_response(
        final_payment_data,
        &state,
        None,
        None,
        header_payload.x_hs_latency,
        &merchant_context,
        &profile,
        None,
    )
}


pub fn is_operation_confirm<Op: Debug>(operation: &Op) -> bool {
    matches!(format!("{operation:?}").as_str(), "PaymentConfirm")
}


pub struct EphemeralKey;


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

use actix_web::{web, Responder};
use hyperswitch_domain_models::merchant_context::{Context, MerchantContext};
use router_env::{instrument, tracing, Flow};

use crate::{
    self as app,
    core::{api_locking, three_ds_decision_rule as three_ds_decision_rule_core},
    services::{api, authentication as auth},
};

#[instrument(skip_all, fields(flow = ?Flow::ThreeDsDecisionRuleExecute))]
#[cfg(feature = "oltp")]
pub async fn execute_decision_rule(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Json<api_models::three_ds_decision_rule::ThreeDsDecisionRuleExecuteRequest>,
) -> impl Responder {
    let flow = Flow::ThreeDsDecisionRuleExecute;
    let payload = payload.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = MerchantContext::NormalMerchant(Box::new(Context(
                auth.merchant_account,
                auth.key_store,
            )));
            three_ds_decision_rule_core::execute_three_ds_decision_rule(
                state,
                merchant_context,
                req,
            )
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


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

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

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

struct CheckoutSeleniumTest;

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

async fn should_make_webhook(web_driver: WebDriver) -> Result<(), WebDriverError> {
    let conn = CheckoutSeleniumTest {};
    conn.make_webhook_test(
        web_driver,
        &format!("{CHECKOUT_BASE_URL}/saved/18"),
        vec![
            Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
            Event::Trigger(Trigger::Sleep(8)),
            Event::Assert(Assert::IsPresent("Google")),
            Event::Assert(Assert::ContainsAny(
                Selector::QueryParamStr,
                vec!["status=succeeded", "status=processing"],
            )),
        ],
        10,
        "succeeded",
    )
    .await?;
    Ok(())
}

#[test]
fn should_make_webhook_test() {
    tester!(should_make_webhook);
}


impl api::PaymentSession for Checkout {}

impl api::PaymentSession for for Checkout


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


pub fn get_billing_merchant_connector_account_id(
        &self,
    ) -> Option<id_type::MerchantConnectorAccountId> {
        self.payment_revenue_recovery_metadata
            .as_ref()
            .map(|recovery_metadata| recovery_metadata.billing_connector_id.clone())
    }


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

pub mod transformers;
use std::sync::LazyLock;

use api_models::webhooks::IncomingWebhookEvent;
use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    errors::api_error_response::ApiErrorResponse,
    payments::payment_attempt::PaymentAttempt,
    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::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, SetupMandateRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorTransactionId, ConnectorValidation,
    },
    configs::Connectors,
    consts::NO_ERROR_CODE,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks::{IncomingWebhook, IncomingWebhookRequestDetails},
};
#[cfg(feature = "v2")]
use masking::PeekInterface;
use masking::{ExposeInterface, Mask};
use transformers as helcim;

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

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

impl Helcim {
    pub fn new() -> &'static Self {
        &Self {
            amount_convertor: &FloatMajorUnitForConnector,
        }
    }
}

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

impl Helcim {
    pub fn connector_transaction_id(
        &self,
        connector_meta: Option<&serde_json::Value>,
    ) -> CustomResult<Option<String>, errors::ConnectorError> {
        let meta: helcim::HelcimMetaData = to_connector_meta(connector_meta.cloned())?;
        Ok(Some(meta.preauth_transaction_id.to_string()))
    }
}

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

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

        //Helcim requires an Idempotency Key of length 25. We prefix every ID by "HS_".
        const ID_LENGTH: usize = 22;
        let mut idempotency_key = vec![(
            headers::IDEMPOTENCY_KEY.to_string(),
            common_utils::generate_id(ID_LENGTH, "HS").into_masked(),
        )];

        header.append(&mut api_key);
        header.append(&mut idempotency_key);
        Ok(header)
    }
}

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

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

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

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::maskable::Maskable<String>)>, errors::ConnectorError>
    {
        let auth = helcim::HelcimAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::API_TOKEN.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: helcim::HelcimErrorResponse = res
            .response
            .parse_struct("HelcimErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        let error_string = match response {
            transformers::HelcimErrorResponse::Payment(response) => match response.errors {
                transformers::HelcimErrorTypes::StringType(error) => error,
                transformers::HelcimErrorTypes::JsonType(error) => error.to_string(),
            },
            transformers::HelcimErrorResponse::General(error_string) => error_string,
        };

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: NO_ERROR_CODE.to_owned(),
            message: error_string.clone(),
            reason: Some(error_string),
            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 Helcim {
    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 Helcim {
    //TODO: implement sessions flow
}

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

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Helcim {
    fn get_headers(
        &self,
        req: &SetupMandateRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::maskable::Maskable<String>)>, errors::ConnectorError>
    {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        _req: &SetupMandateRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}v2/payment/verify", self.base_url(connectors)))
    }
    fn get_request_body(
        &self,
        req: &SetupMandateRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = helcim::HelcimVerifyRequest::try_from(req)?;

        Ok(RequestContent::Json(Box::new(connector_req)))
    }
    fn build_request(
        &self,
        _req: &SetupMandateRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(
            errors::ConnectorError::NotImplemented("Setup Mandate flow for Helcim".to_string())
                .into(),
        )

        // Ok(Some(
        //     RequestBuilder::new()
        //         .method(Method::Post)
        //         .url(&types::SetupMandateType::get_url(self, req, connectors)?)
        //         .attach_default_headers()
        //         .headers(types::SetupMandateType::get_headers(self, req, connectors)?)
        //         .set_body(types::SetupMandateType::get_request_body(
        //             self, req, connectors,
        //         )?)
        //         .build(),
        // ))
    }
    fn handle_response(
        &self,
        data: &SetupMandateRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<SetupMandateRouterData, errors::ConnectorError> {
        let response: helcim::HelcimPaymentsResponse = res
            .response
            .parse_struct("Helcim 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<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Helcim {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::maskable::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> {
        if req.request.is_auto_capture()? {
            return Ok(format!("{}v2/payment/purchase", self.base_url(connectors)));
        }
        Ok(format!("{}v2/payment/preauth", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_router_data = convert_amount(
            self.amount_convertor,
            req.request.minor_amount,
            req.request.currency,
        )?;
        let router_obj = helcim::HelcimRouterData::try_from((connector_router_data, req))?;
        let connector_req = helcim::HelcimPaymentsRequest::try_from(&router_obj)?;
        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(&types::PaymentsAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::PaymentsAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::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: helcim::HelcimPaymentsResponse = res
            .response
            .parse_struct("Helcim 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 Helcim {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::maskable::Maskable<String>)>, errors::ConnectorError>
    {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            types::PaymentsSyncType::get_content_type(self)
                .to_string()
                .into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

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

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_payment_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;

        Ok(format!(
            "{}v2/card-transactions/{connector_payment_id}",
            self.base_url(connectors)
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::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: helcim::HelcimPaymentsResponse = res
            .response
            .parse_struct("helcim 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)
    }

    // fn get_multiple_capture_sync_method(
    //     &self,
    // ) -> CustomResult<services::CaptureSyncMethod, errors::ConnectorError> {
    //     Ok(services::CaptureSyncMethod::Individual)
    // }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Helcim {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::maskable::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> {
        Ok(format!("{}v2/payment/capture", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_router_data = convert_amount(
            self.amount_convertor,
            req.request.minor_amount_to_capture,
            req.request.currency,
        )?;
        let router_obj = helcim::HelcimRouterData::try_from((connector_router_data, req))?;
        let connector_req = helcim::HelcimCaptureRequest::try_from(&router_obj)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsCaptureType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::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: helcim::HelcimPaymentsResponse = res
            .response
            .parse_struct("Helcim 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 Helcim {
    fn get_headers(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::maskable::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: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}v2/payment/reverse", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = helcim::HelcimVoidRequest::try_from(req)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsCancelRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCancelRouterData, errors::ConnectorError> {
        let response: helcim::HelcimPaymentsResponse = res
            .response
            .parse_struct("HelcimPaymentsResponse")
            .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,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

    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<Execute, RefundsData, RefundsResponseData> for Helcim {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::maskable::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> {
        Ok(format!("{}v2/payment/refund", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_router_data = convert_amount(
            self.amount_convertor,
            req.request.minor_refund_amount,
            req.request.currency,
        )?;
        let router_obj = helcim::HelcimRouterData::try_from((connector_router_data, req))?;
        let connector_req = helcim::HelcimRefundRequest::try_from(&router_obj)?;
        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(&types::RefundExecuteType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(types::RefundExecuteType::get_headers(
                self, req, connectors,
            )?)
            .set_body(types::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: helcim::RefundResponse =
            res.response
                .parse_struct("helcim 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 Helcim {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::maskable::Maskable<String>)>, errors::ConnectorError>
    {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            types::RefundSyncType::get_content_type(self)
                .to_string()
                .into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

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

    fn get_url(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_refund_id = req
            .request
            .connector_refund_id
            .clone()
            .ok_or(errors::ConnectorError::MissingConnectorRefundID)?;

        Ok(format!(
            "{}v2/card-transactions/{connector_refund_id}",
            self.base_url(connectors)
        ))
    }

    fn build_request(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::RefundSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::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: helcim::RefundResponse = res
            .response
            .parse_struct("helcim 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 IncomingWebhook for Helcim {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

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

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

static HELCIM_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> = LazyLock::new(|| {
    let supported_capture_methods = vec![
        enums::CaptureMethod::Automatic,
        enums::CaptureMethod::Manual,
        enums::CaptureMethod::SequentialAutomatic,
    ];

    let supported_card_network = vec![
        common_enums::CardNetwork::Visa,
        common_enums::CardNetwork::Mastercard,
        common_enums::CardNetwork::Interac,
        common_enums::CardNetwork::AmericanExpress,
        common_enums::CardNetwork::JCB,
        common_enums::CardNetwork::DinersClub,
        common_enums::CardNetwork::Discover,
        common_enums::CardNetwork::CartesBancaires,
        common_enums::CardNetwork::UnionPay,
    ];

    let mut helcim_supported_payment_methods = SupportedPaymentMethods::new();

    helcim_supported_payment_methods.add(
        enums::PaymentMethod::Card,
        enums::PaymentMethodType::Credit,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::Supported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: Some(
                api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                    api_models::feature_matrix::CardSpecificFeatures {
                        three_ds: common_enums::FeatureStatus::NotSupported,
                        no_three_ds: common_enums::FeatureStatus::Supported,
                        supported_card_networks: supported_card_network.clone(),
                    }
                }),
            ),
        },
    );

    helcim_supported_payment_methods.add(
        enums::PaymentMethod::Card,
        enums::PaymentMethodType::Debit,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::Supported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: Some(
                api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                    api_models::feature_matrix::CardSpecificFeatures {
                        three_ds: common_enums::FeatureStatus::NotSupported,
                        no_three_ds: common_enums::FeatureStatus::Supported,
                        supported_card_networks: supported_card_network.clone(),
                    }
                }),
            ),
        },
    );

    helcim_supported_payment_methods
});

static HELCIM_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Helcim",
        description:
            "Helcim is a payment processing company that offers transparent, affordable merchant services for businesses of all sizes",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Sandbox,
    };

static HELCIM_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = [];

impl ConnectorSpecifications for Helcim {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&HELCIM_CONNECTOR_INFO)
    }

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

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

impl ConnectorTransactionId for Helcim {
    #[cfg(feature = "v1")]
    fn connector_transaction_id(
        &self,
        payment_attempt: &PaymentAttempt,
    ) -> Result<Option<String>, ApiErrorResponse> {
        if payment_attempt.get_connector_payment_id().is_none() {
            let metadata =
                Self::connector_transaction_id(self, payment_attempt.connector_metadata.as_ref());
            metadata.map_err(|_| ApiErrorResponse::ResourceIdNotFound)
        } else {
            Ok(payment_attempt
                .get_connector_payment_id()
                .map(ToString::to_string))
        }
    }

    #[cfg(feature = "v2")]
    fn connector_transaction_id(
        &self,
        payment_attempt: &PaymentAttempt,
    ) -> Result<Option<String>, ApiErrorResponse> {
        use hyperswitch_domain_models::errors::api_error_response::ApiErrorResponse;

        if payment_attempt.get_connector_payment_id().is_none() {
            let metadata = Self::connector_transaction_id(
                self,
                payment_attempt
                    .connector_metadata
                    .as_ref()
                    .map(|connector_metadata| connector_metadata.peek()),
            );
            metadata.map_err(|_| ApiErrorResponse::ResourceIdNotFound)
        } else {
            Ok(payment_attempt
                .get_connector_payment_id()
                .map(ToString::to_string))
        }
    }
}


Documentation: api-reference/v1/customer-set-default-payment-method/customers--set-default-payment-method.mdx
# Type: Doc File

---
openapi: post /{customer_id}/payment_methods/{payment_method_id}/default
---


pub struct ConsumerAuthenticationResponse {
    code: Option<String>,
    code_raw: Option<String>,
}


impl PaymentMetricAccumulator for RetriesAmountAccumulator {
    type MetricOutput = Option<u64>;
    fn add_metrics_bucket(&mut self, metrics: &PaymentMetricRow) {
        self.total = match (
            self.total,
            metrics.total.as_ref().and_then(ToPrimitive::to_i64),
        ) {
            (None, None) => None,
            (None, i @ Some(_)) | (i @ Some(_), None) => i,
            (Some(a), Some(b)) => Some(a + b),
        };
    }
    #[inline]
    fn collect(self) -> Self::MetricOutput {
        u64::try_from(self.total.unwrap_or(0)).ok()
    }
}

impl PaymentMetricAccumulator for for RetriesAmountAccumulator


impl api::MandateSetup for Placetopay {}

impl api::MandateSetup for for Placetopay


pub struct CardAndNetworkTokenData {
    pub card_data: payment_method_data::Card,
    pub network_token_data: payment_method_data::NetworkTokenData,
}


pub struct AdyenTransferResponse {
    id: String,
    account_holder: AdyenPlatformAccountHolder,
    amount: adyen::Amount,
    balance_account: AdyenBalanceAccount,
    category: AdyenPayoutMethod,
    category_data: Option<AdyenCategoryData>,
    direction: AdyenTransactionDirection,
    reference: String,
    reference_for_beneficiary: String,
    status: AdyenTransferStatus,
    #[serde(rename = "type")]
    transaction_type: AdyenTransactionType,
    reason: String,
}


impl ProxyClient {
    pub fn new(proxy_config: &Proxy) -> CustomResult<Self, ApiClientError> {
        let client = client::get_client_builder(proxy_config)
            .switch()?
            .build()
            .change_context(ApiClientError::InvalidProxyConfiguration)?;
        Ok(Self {
            proxy_config: proxy_config.clone(),
            client,
            request_id: None,
        })
    }

    pub fn get_reqwest_client(
        &self,
        client_certificate: Option<masking::Secret<String>>,
        client_certificate_key: Option<masking::Secret<String>>,
    ) -> CustomResult<reqwest::Client, ApiClientError> {
        match (client_certificate, client_certificate_key) {
            (Some(certificate), Some(certificate_key)) => {
                let client_builder = client::get_client_builder(&self.proxy_config).switch()?;
                let identity =
                    client::create_identity_from_certificate_and_key(certificate, certificate_key)
                        .switch()?;
                Ok(client_builder
                    .identity(identity)
                    .build()
                    .change_context(ApiClientError::ClientConstructionFailed)
                    .attach_printable(
                        "Failed to construct client with certificate and certificate key",
                    )?)
            }
            (_, _) => Ok(self.client.clone()),
        }
    }
}


/// Config path.
    pub fn config_path(environment: &str, explicit_config_path: Option<PathBuf>) -> PathBuf {
        let mut config_path = PathBuf::new();
        if let Some(explicit_config_path_val) = explicit_config_path {
            config_path.push(explicit_config_path_val);
        } else {
            let config_file_name = match environment {
                "production" => "production.toml",
                "sandbox" => "sandbox.toml",
                _ => "development.toml",
            };

            let config_directory = Self::get_config_directory();
            config_path.push(config_directory);
            config_path.push(config_file_name);
        }
        config_path
    }


File: crates/analytics/src/frm/metrics/frm_triggered_attempts.rs

use api_models::analytics::{
    frm::{FrmDimensions, FrmFilters, FrmMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

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

#[derive(Default)]
pub(super) struct FrmTriggeredAttempts {}

#[async_trait::async_trait]
impl<T> super::FrmMetric<T> for FrmTriggeredAttempts
where
    T: AnalyticsDataSource + super::FrmMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[FrmDimensions],
        merchant_id: &common_utils::id_type::MerchantId,
        filters: &FrmFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<Vec<(FrmMetricsBucketIdentifier, FrmMetricRow)>> {
        let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::FraudCheck);

        for dim in dimensions.iter() {
            query_builder.add_select_column(dim).switch()?;
        }

        query_builder
            .add_select_column(Aggregate::Count {
                field: None,
                alias: Some("count"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;
        query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

        filters.set_filter_clause(&mut query_builder).switch()?;

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

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

        for dim in dimensions.iter() {
            query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }

        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

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


impl WorldpayErrorResponse {
    pub fn default(status_code: u16) -> Self {
        match status_code {
            code @ 404 => Self {
                error_name: format!("{code} Not found"),
                message: "Resource not found".to_string(),
                validation_errors: None,
            },
            code => Self {
                error_name: code.to_string(),
                message: "Unknown error".to_string(),
                validation_errors: None,
            },
        }
    }
}


pub struct UpiCollectData {
    pub vpa_id: Option<Secret<String, pii::UpiVpaMaskingStrategy>>,
}


pub async fn create_new_merchant_and_insert_in_db(
        &self,
        state: SessionState,
    ) -> UserResult<domain::MerchantAccount> {
        self.check_if_already_exists_in_db(state.clone()).await?;

        let merchant_account_create_request = self
            .create_merchant_account_request()
            .attach_printable("unable to construct merchant account create request")?;

        let ApplicationResponse::Json(merchant_account_response) = Box::pin(
            admin::create_merchant_account(state.clone(), merchant_account_create_request, None),
        )
        .await
        .change_context(UserErrors::InternalServerError)
        .attach_printable("Error while creating a merchant")?
        else {
            return Err(UserErrors::InternalServerError.into());
        };

        let profile_create_request = admin_api::ProfileCreate {
            profile_name: consts::user::DEFAULT_PROFILE_NAME.to_string(),
            ..Default::default()
        };

        let key_manager_state = &(&state).into();
        let merchant_key_store = state
            .store
            .get_merchant_key_store_by_merchant_id(
                key_manager_state,
                &merchant_account_response.id,
                &state.store.get_master_key().to_vec().into(),
            )
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Failed to retrieve merchant key store by merchant_id")?;

        let merchant_account = state
            .store
            .find_merchant_account_by_merchant_id(
                key_manager_state,
                &merchant_account_response.id,
                &merchant_key_store,
            )
            .await
            .change_context(UserErrors::InternalServerError)
            .attach_printable("Failed to retrieve merchant account by merchant_id")?;

        let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
            merchant_account.clone(),
            merchant_key_store,
        )));

        Box::pin(admin::create_profile(
            state,
            profile_create_request,
            merchant_context,
        ))
        .await
        .change_context(UserErrors::InternalServerError)
        .attach_printable("Error while creating a profile")?;
        Ok(merchant_account)
    }


impl AuthipayPaymentsResponse {
    /// Get gateway response (like Fiserv's gateway_response)
    pub fn gateway_response(&self) -> AuthipayGatewayResponse {
        AuthipayGatewayResponse {
            transaction_state: self.get_transaction_status(),
            transaction_processing_details: AuthipayTransactionProcessingDetails {
                order_id: self.order_id.clone(),
                transaction_id: self.ipg_transaction_id.clone(),
            },
        }
    }

    /// Get payment receipt (like Fiserv's payment_receipt)
    pub fn payment_receipt(&self) -> AuthipayPaymentReceipt {
        AuthipayPaymentReceipt {
            approved_amount: self.approved_amount.clone(),
            processor_response_details: Some(self.processor.clone()),
        }
    }

    /// Determine the transaction status based on transaction type and various status fields (like Fiserv)
    fn get_transaction_status(&self) -> AuthipayTransactionStatus {
        match self.transaction_type.as_str() {
            "RETURN" => {
                // Refund transaction - use transaction_result
                match self.transaction_result.as_deref() {
                    Some("APPROVED") => AuthipayTransactionStatus::Captured,
                    Some("DECLINED") | Some("FAILED") => AuthipayTransactionStatus::Failed,
                    _ => AuthipayTransactionStatus::Processing,
                }
            }
            "VOID" => {
                // Void transaction - use transaction_result, fallback to transaction_state
                match self.transaction_result.as_deref() {
                    Some("APPROVED") => AuthipayTransactionStatus::Voided,
                    Some("DECLINED") | Some("FAILED") => AuthipayTransactionStatus::Failed,
                    Some("PENDING") | Some("PROCESSING") => AuthipayTransactionStatus::Processing,
                    _ => {
                        // Fallback to transaction_state for void operations
                        match self.transaction_state.as_deref() {
                            Some("VOIDED") => AuthipayTransactionStatus::Voided,
                            Some("FAILED") | Some("DECLINED") => AuthipayTransactionStatus::Failed,
                            _ => AuthipayTransactionStatus::Voided, // Default assumption for void requests
                        }
                    }
                }
            }
            _ => {
                // Payment transaction - prioritize transaction_state over transaction_status
                match self.transaction_state.as_deref() {
                    Some("AUTHORIZED") => AuthipayTransactionStatus::Authorized,
                    Some("CAPTURED") => AuthipayTransactionStatus::Captured,
                    Some("VOIDED") => AuthipayTransactionStatus::Voided,
                    Some("DECLINED") | Some("FAILED") => AuthipayTransactionStatus::Failed,
                    _ => {
                        // Fallback to transaction_status with transaction_type context
                        match (
                            self.transaction_type.as_str(),
                            self.transaction_status.as_deref(),
                        ) {
                            // For PREAUTH transactions, "APPROVED" means authorized and awaiting capture
                            ("PREAUTH", Some("APPROVED")) => AuthipayTransactionStatus::Authorized,
                            // For POSTAUTH transactions, "APPROVED" means successfully captured
                            ("POSTAUTH", Some("APPROVED")) => AuthipayTransactionStatus::Captured,
                            // For SALE transactions, "APPROVED" means completed payment
                            ("SALE", Some("APPROVED")) => AuthipayTransactionStatus::Captured,
                            // For VOID transactions, "APPROVED" means successfully voided
                            ("VOID", Some("APPROVED")) => AuthipayTransactionStatus::Voided,
                            // Generic status mappings for other cases
                            (_, Some("APPROVED")) => AuthipayTransactionStatus::Captured,
                            (_, Some("AUTHORIZED")) => AuthipayTransactionStatus::Authorized,
                            (_, Some("DECLINED") | Some("FAILED")) => {
                                AuthipayTransactionStatus::Failed
                            }
                            _ => AuthipayTransactionStatus::Processing,
                        }
                    }
                }
            }
        }
    }
}


pub async fn build_and_send_decision_engine_http_request<Req, Res, ErrRes>(
    state: &SessionState,
    http_method: services::Method,
    path: &str,
    request_body: Option<Req>,
    _timeout: Option<u64>,
    context_message: &str,
    events_wrapper: Option<RoutingEventsWrapper<Req>>,
) -> RoutingResult<RoutingEventsResponse<Res>>
where
    Req: Serialize + Send + Sync + 'static + Clone,
    Res: Serialize + serde::de::DeserializeOwned + std::fmt::Debug + Clone + 'static,
    ErrRes: serde::de::DeserializeOwned + std::fmt::Debug + Clone + DecisionEngineErrorsInterface,
{
    let decision_engine_base_url = &state.conf.open_router.url;
    let url = format!("{decision_engine_base_url}/{path}");
    logger::debug!(decision_engine_api_call_url = %url, decision_engine_request_path = %path, http_method = ?http_method, "decision_engine: Initiating decision_engine API call ({})", context_message);

    let mut request_builder = services::RequestBuilder::new()
        .method(http_method)
        .url(&url);

    if let Some(body_content) = request_body {
        let body = common_utils::request::RequestContent::Json(Box::new(body_content));
        request_builder = request_builder.set_body(body);
    }

    let http_request = request_builder.build();
    logger::info!(?http_request, decision_engine_request_path = %path, "decision_engine: Constructed Decision Engine API request details ({})", context_message);
    let should_parse_response = events_wrapper
        .as_ref()
        .map(|wrapper| wrapper.parse_response)
        .unwrap_or(true);

    let closure = || async {
        let response =
            services::call_connector_api(state, http_request, "Decision Engine API call")
                .await
                .change_context(errors::RoutingError::OpenRouterCallFailed)?;

        match response {
            Ok(resp) => {
                logger::debug!(
                    "decision_engine: Received response from Decision Engine API ({:?})",
                    String::from_utf8_lossy(&resp.response) // For logging
                );

                let resp = should_parse_response
                    .then(|| {
                        if std::any::TypeId::of::<Res>() == std::any::TypeId::of::<String>()
                            && resp.response.is_empty()
                        {
                            return serde_json::from_str::<Res>("\"\"").change_context(
                                errors::RoutingError::OpenRouterError(
                                    "Failed to parse empty response as String".into(),
                                ),
                            );
                        }
                        let response_type: Res = resp
                            .response
                            .parse_struct(std::any::type_name::<Res>())
                            .change_context(errors::RoutingError::OpenRouterError(
                                "Failed to parse the response from open_router".into(),
                            ))?;

                        Ok::<_, error_stack::Report<errors::RoutingError>>(response_type)
                    })
                    .transpose()?;

                logger::debug!("decision_engine_success_response: {:?}", resp);

                Ok(resp)
            }
            Err(err) => {
                logger::debug!(
                    "decision_engine: Received response from Decision Engine API ({:?})",
                    String::from_utf8_lossy(&err.response) // For logging
                );

                let err_resp: ErrRes = err
                    .response
                    .parse_struct(std::any::type_name::<ErrRes>())
                    .change_context(errors::RoutingError::OpenRouterError(
                    "Failed to parse the response from open_router".into(),
                ))?;

                logger::error!(
                    decision_engine_error_code = %err_resp.get_error_code(),
                    decision_engine_error_message = %err_resp.get_error_message(),
                    decision_engine_raw_response = ?err_resp.get_error_data(),
                );

                Err(error_stack::report!(
                    errors::RoutingError::RoutingEventsError {
                        message: err_resp.get_error_message(),
                        status_code: err.status_code,
                    }
                ))
            }
        }
    };

    let events_response = if let Some(wrapper) = events_wrapper {
        wrapper
            .construct_event_builder(
                url,
                routing_events::RoutingEngine::DecisionEngine,
                routing_events::ApiMethod::Rest(http_method),
            )?
            .trigger_event(state, closure)
            .await?
    } else {
        let resp = closure()
            .await
            .change_context(errors::RoutingError::OpenRouterCallFailed)?;

        RoutingEventsResponse::new(None, resp)
    };

    Ok(events_response)
}


impl api::RefundSync for Trustpay {}


impl ConnectorValidation for Rapyd {}


pub async fn get_routing_events<T>(
    merchant_id: &common_utils::id_type::MerchantId,
    query_param: RoutingEventsRequest,
    pool: &T,
) -> FiltersResult<Vec<RoutingEventsResult>>
where
    T: AnalyticsDataSource + RoutingEventLogAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::RoutingEvents);
    query_builder.add_select_column("*").switch()?;

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

    query_builder
        .add_filter_clause("payment_id", &query_param.payment_id)
        .switch()?;

    if let Some(refund_id) = query_param.refund_id {
        query_builder
            .add_filter_clause("refund_id", &refund_id)
            .switch()?;
    }

    if let Some(dispute_id) = query_param.dispute_id {
        query_builder
            .add_filter_clause("dispute_id", &dispute_id)
            .switch()?;
    }

    query_builder
        .execute_query::<RoutingEventsResult, _>(pool)
        .await
        .change_context(FiltersError::QueryBuildingError)?
        .change_context(FiltersError::QueryExecutionFailure)
}


pub async fn invalidate(
    state: web::Data<AppState>,
    req: HttpRequest,
    key: web::Path<String>,
) -> impl Responder {
    let flow = Flow::CacheInvalidate;

    let key = key.into_inner().to_owned();

    api::server_wrap(
        flow,
        state,
        &req,
        &key,
        |state, _, key, _| cache::invalidate(state, key),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    )
    .await
}


pub async fn check_email_token_in_blacklist(state: &SessionState, token: &str) -> UserResult<()> {
    let redis_conn = get_redis_connection_for_global_tenant(state)
        .change_context(UserErrors::InternalServerError)?;
    let blacklist_key = format!("{EMAIL_TOKEN_BLACKLIST_PREFIX}{token}");
    let key_exists = redis_conn
        .exists::<()>(&blacklist_key.as_str().into())
        .await
        .change_context(UserErrors::InternalServerError)?;

    if key_exists {
        return Err(UserErrors::LinkInvalid.into());
    }
    Ok(())
}


impl api::PaymentSession for Hyperwallet {}

impl api::PaymentSession for for Hyperwallet


pub struct KVRouterStore<T: DatabaseStore> {
    pub router_store: RouterStore<T>,
    drainer_stream_name: String,
    drainer_num_partitions: u8,
    pub ttl_for_kv: u32,
    pub request_id: Option<String>,
    pub soft_kill_mode: bool,
}


pub fn generate_newman_command_for_connector() -> Result<ReturnArgs> {
    let args = Args::parse();

    let connector_name = args
        .connector_name
        .with_context(|| "invalid parameters: connector/module name not found in arguments")?;

    let base_url = args.base_url;
    let admin_api_key = args.admin_api_key;

    let collection_path = get_collection_path(&connector_name);
    let collection_dir_path = get_dir_path(&connector_name);
    let auth_map = ConnectorAuthenticationMap::new();

    let inner_map = auth_map.inner();

    /*
    Newman runner
    Certificate keys are added through secrets in CI, so there's no need to explicitly pass it as arguments.
    It can be overridden by explicitly passing certificates as arguments.

    If the collection requires certificates (Stripe collection for example) during the merchant connector account create step,
    then Stripe's certificates will be passed implicitly (for now).
    If any other connector requires certificates to be passed, that has to be passed explicitly for now.
    */

    let mut newman_command = Command::new("newman");
    newman_command.args(["run", &collection_path]);
    newman_command.args(["--env-var", &format!("admin_api_key={admin_api_key}")]);
    newman_command.args(["--env-var", &format!("baseUrl={base_url}")]);

    let custom_header_exist = check_for_custom_headers(args.custom_headers, &collection_dir_path);

    // validation of connector is needed here as a work around to the limitation of the fork of newman that Hyperswitch uses
    let (connector_name, modified_collection_file_paths) =
        check_connector_for_dynamic_amount(&connector_name);

    if let Some(auth_type) = inner_map.get(connector_name) {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => {
                newman_command.args([
                    "--env-var",
                    &format!("connector_api_key={}", api_key.peek()),
                ]);
            }
            ConnectorAuthType::BodyKey { api_key, key1 } => {
                newman_command.args([
                    "--env-var",
                    &format!("connector_api_key={}", api_key.peek()),
                    "--env-var",
                    &format!("connector_key1={}", key1.peek()),
                ]);
            }
            ConnectorAuthType::SignatureKey {
                api_key,
                key1,
                api_secret,
            } => {
                newman_command.args([
                    "--env-var",
                    &format!("connector_api_key={}", api_key.peek()),
                    "--env-var",
                    &format!("connector_key1={}", key1.peek()),
                    "--env-var",
                    &format!("connector_api_secret={}", api_secret.peek()),
                ]);
            }
            ConnectorAuthType::MultiAuthKey {
                api_key,
                key1,
                key2,
                api_secret,
            } => {
                newman_command.args([
                    "--env-var",
                    &format!("connector_api_key={}", api_key.peek()),
                    "--env-var",
                    &format!("connector_key1={}", key1.peek()),
                    "--env-var",
                    &format!("connector_key2={}", key2.peek()),
                    "--env-var",
                    &format!("connector_api_secret={}", api_secret.peek()),
                ]);
            }
            // Handle other ConnectorAuthType variants
            _ => {
                eprintln!("Invalid authentication type.");
            }
        }
    } else {
        eprintln!("Connector not found.");
    }

    // Add additional environment variables if present
    if let Ok(gateway_merchant_id) = env::var("GATEWAY_MERCHANT_ID") {
        newman_command.args([
            "--env-var",
            &format!("gateway_merchant_id={gateway_merchant_id}"),
        ]);
    }

    if let Ok(gpay_certificate) = env::var("GPAY_CERTIFICATE") {
        newman_command.args(["--env-var", &format!("certificate={gpay_certificate}")]);
    }

    if let Ok(gpay_certificate_keys) = env::var("GPAY_CERTIFICATE_KEYS") {
        newman_command.args([
            "--env-var",
            &format!("certificate_keys={gpay_certificate_keys}"),
        ]);
    }

    if let Ok(merchant_api_key) = env::var("MERCHANT_API_KEY") {
        newman_command.args(["--env-var", &format!("merchant_api_key={merchant_api_key}")]);
    }

    newman_command.args([
        "--delay-request",
        format!("{}", &args.delay_request).as_str(),
    ]);

    newman_command.arg("--color").arg("on");

    // Add flags for running specific folders
    if let Some(folders) = &args.folders {
        let folder_names: Vec<String> = folders.split(',').map(|s| s.trim().to_string()).collect();

        for folder_name in folder_names {
            if !folder_name.contains("QuickStart") {
                // This is a quick fix, "QuickStart" is intentional to have merchant account and API keys set up
                // This will be replaced by a more robust and efficient account creation or reuse existing old account
                newman_command.args(["--folder", "QuickStart"]);
            }
            newman_command.args(["--folder", &folder_name]);
        }
    }

    if args.verbose {
        newman_command.arg("--verbose");
    }

    Ok(ReturnArgs {
        newman_command,
        modified_file_paths: vec![modified_collection_file_paths, custom_header_exist],
        collection_path,
    })
}


impl events::ApiEventMetric for DecisionManagerRequest {
    fn get_api_event_type(&self) -> Option<events::ApiEventsType> {
        Some(events::ApiEventsType::Routing)
    }
}

impl events::ApiEventMetric for for DecisionManagerRequest