fn handle_bank_redirects_error_response(
    response: ErrorResponseBankRedirect,
    status_code: u16,
    previous_attempt_status: enums::AttemptStatus,
) -> CustomResult<
    (
        enums::AttemptStatus,
        Option<ErrorResponse>,
        PaymentsResponseData,
    ),
    errors::ConnectorError,
> {
    let status = if matches!(response.payment_result_info.result_code, 1132014 | 1132005) {
        previous_attempt_status
    } else {
        enums::AttemptStatus::AuthorizationFailed
    };
    let error = Some(ErrorResponse {
        code: response.payment_result_info.result_code.to_string(),
        // message vary for the same code, so relying on code alone as it is unique
        message: response.payment_result_info.result_code.to_string(),
        reason: response.payment_result_info.additional_info,
        status_code,
        attempt_status: Some(status),
        connector_transaction_id: None,
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    });
    let payment_response_data = PaymentsResponseData::TransactionResponse {
        resource_id: ResponseId::NoResponseId,
        redirection_data: Box::new(None),
        mandate_reference: Box::new(None),
        connector_metadata: None,
        network_txn_id: None,
        connector_response_reference_id: None,
        incremental_authorization_allowed: None,
        charges: None,
    };
    Ok((status, error, payment_response_data))
}

fn handle_bank_redirects_sync_response(
    response: SyncResponseBankRedirect,
    status_code: u16,
) -> CustomResult<
    (
        enums::AttemptStatus,
        Option<ErrorResponse>,
        PaymentsResponseData,
    ),
    errors::ConnectorError,
> {
    let status = enums::AttemptStatus::from(response.payment_information.status);
    let error = if utils::is_payment_failure(status) {
        let reason_info = response
            .payment_information
            .status_reason_information
            .unwrap_or_default();
        Some(ErrorResponse {
            code: reason_info
                .reason
                .code
                .clone()
                .unwrap_or(consts::NO_ERROR_CODE.to_string()),
            // message vary for the same code, so relying on code alone as it is unique
            message: reason_info
                .reason
                .code
                .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
            reason: reason_info.reason.reject_reason,
            status_code,
            attempt_status: None,
            connector_transaction_id: Some(
                response
                    .payment_information
                    .references
                    .payment_request_id
                    .clone(),
            ),
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    } else {
        None
    };
    let payment_response_data = PaymentsResponseData::TransactionResponse {
        resource_id: ResponseId::ConnectorTransactionId(
            response
                .payment_information
                .references
                .payment_request_id
                .clone(),
        ),
        redirection_data: Box::new(None),
        mandate_reference: Box::new(None),
        connector_metadata: None,
        network_txn_id: None,
        connector_response_reference_id: None,
        incremental_authorization_allowed: None,
        charges: None,
    };
    Ok((status, error, payment_response_data))
}

pub fn handle_webhook_response(
    payment_information: WebhookPaymentInformation,
    status_code: u16,
) -> CustomResult<
    (
        enums::AttemptStatus,
        Option<ErrorResponse>,
        PaymentsResponseData,
    ),
    errors::ConnectorError,
> {
    let status = enums::AttemptStatus::try_from(payment_information.status)?;
    let error = if utils::is_payment_failure(status) {
        let reason_info = payment_information
            .status_reason_information
            .unwrap_or_default();
        Some(ErrorResponse {
            code: reason_info
                .reason
                .code
                .clone()
                .unwrap_or(consts::NO_ERROR_CODE.to_string()),
            // message vary for the same code, so relying on code alone as it is unique
            message: reason_info
                .reason
                .code
                .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
            reason: reason_info.reason.reject_reason,
            status_code,
            attempt_status: None,
            connector_transaction_id: payment_information.references.payment_request_id.clone(),
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    } else {
        None
    };
    let payment_response_data = PaymentsResponseData::TransactionResponse {
        resource_id: ResponseId::NoResponseId,
        redirection_data: Box::new(None),
        mandate_reference: Box::new(None),
        connector_metadata: None,
        network_txn_id: None,
        connector_response_reference_id: None,
        incremental_authorization_allowed: None,
        charges: None,
    };
    Ok((status, error, payment_response_data))
}

pub fn get_trustpay_response(
    response: TrustpayPaymentsResponse,
    status_code: u16,
    previous_attempt_status: enums::AttemptStatus,
) -> CustomResult<
    (
        enums::AttemptStatus,
        Option<ErrorResponse>,
        PaymentsResponseData,
    ),
    errors::ConnectorError,
> {
    match response {
        TrustpayPaymentsResponse::CardsPayments(response) => {
            handle_cards_response(*response, status_code)
        }
        TrustpayPaymentsResponse::BankRedirectPayments(response) => {
            handle_bank_redirects_response(*response)
        }
        TrustpayPaymentsResponse::BankRedirectSync(response) => {
            handle_bank_redirects_sync_response(*response, status_code)
        }
        TrustpayPaymentsResponse::BankRedirectError(response) => {
            handle_bank_redirects_error_response(*response, status_code, previous_attempt_status)
        }
        TrustpayPaymentsResponse::WebhookResponse(response) => {
            handle_webhook_response(*response, status_code)
        }
    }
}

#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct TrustpayAuthUpdateRequest {
    pub grant_type: String,
}

impl TryFrom<&RefreshTokenRouterData> for TrustpayAuthUpdateRequest {
    type Error = Error;
    fn try_from(_item: &RefreshTokenRouterData) -> Result<Self, Self::Error> {
        Ok(Self {
            grant_type: "client_credentials".to_string(),
        })
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct ResultInfo {
    pub result_code: i64,
    pub additional_info: Option<String>,
    pub correlation_id: Option<String>,
}

#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct TrustpayAuthUpdateResponse {
    pub access_token: Option<Secret<String>>,
    pub token_type: Option<String>,
    pub expires_in: Option<i64>,
    #[serde(rename = "ResultInfo")]
    pub result_info: ResultInfo,
}

#[derive(Default, Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct TrustpayAccessTokenErrorResponse {
    pub result_info: ResultInfo,
}

impl<F, T> TryFrom<ResponseRouterData<F, TrustpayAuthUpdateResponse, T, AccessToken>>
    for RouterData<F, T, AccessToken>
{
    type Error = Error;
    fn try_from(
        item: ResponseRouterData<F, TrustpayAuthUpdateResponse, T, AccessToken>,
    ) -> Result<Self, Self::Error> {
        match (item.response.access_token, item.response.expires_in) {
            (Some(access_token), Some(expires_in)) => Ok(Self {
                response: Ok(AccessToken {
                    token: access_token,
                    expires: expires_in,
                }),
                ..item.data
            }),
            _ => Ok(Self {
                response: Err(ErrorResponse {
                    code: item.response.result_info.result_code.to_string(),
                    // message vary for the same code, so relying on code alone as it is unique
                    message: item.response.result_info.result_code.to_string(),
                    reason: item.response.result_info.additional_info,
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                }),
                ..item.data
            }),
        }
    }
}

#[derive(Default, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TrustpayCreateIntentRequest {
    pub amount: StringMajorUnit,
    pub currency: String,
    // If true, Apple Pay will be initialized
    pub init_apple_pay: Option<bool>,
    // If true, Google pay will be initialized
    pub init_google_pay: Option<bool>,
    pub reference: String,
}

impl TryFrom<&TrustpayRouterData<&PaymentsPreProcessingRouterData>>
    for TrustpayCreateIntentRequest
{
    type Error = Error;
    fn try_from(
        item: &TrustpayRouterData<&PaymentsPreProcessingRouterData>,
    ) -> Result<Self, Self::Error> {
        let is_apple_pay = item
            .router_data
            .request
            .payment_method_type
            .as_ref()
            .map(|pmt| matches!(pmt, enums::PaymentMethodType::ApplePay));

        let is_google_pay = item
            .router_data
            .request
            .payment_method_type
            .as_ref()
            .map(|pmt| matches!(pmt, enums::PaymentMethodType::GooglePay));

        let currency = item.router_data.request.get_currency()?;
        let amount = item.amount.to_owned();

        Ok(Self {
            amount,
            currency: currency.to_string(),
            init_apple_pay: is_apple_pay,
            init_google_pay: is_google_pay,
            reference: item.router_data.connector_request_reference_id.clone(),
        })
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TrustpayCreateIntentResponse {
    // TrustPay's authorization secrets used by client
    pub secrets: SdkSecretInfo,
    // 	Data object to be used for Apple Pay or Google Pay
    #[serde(flatten)]
    pub init_result_data: InitResultData,
    // Unique operation/transaction identifier
    pub instance_id: String,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum InitResultData {
    AppleInitResultData(TrustpayApplePayResponse),
    GoogleInitResultData(TrustpayGooglePayResponse),
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayTransactionInfo {
    pub country_code: api_models::enums::CountryAlpha2,
    pub currency_code: api_models::enums::Currency,
    pub total_price_status: String,
    pub total_price: StringMajorUnit,
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayMerchantInfo {
    pub merchant_name: Secret<String>,
    pub merchant_id: Secret<String>,
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayAllowedPaymentMethods {
    #[serde(rename = "type")]
    pub payment_method_type: String,
    pub parameters: GpayAllowedMethodsParameters,
    pub tokenization_specification: GpayTokenizationSpecification,
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GpayTokenParameters {
    pub gateway: String,
    pub gateway_merchant_id: Secret<String>,
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GpayTokenizationSpecification {
    #[serde(rename = "type")]
    pub token_specification_type: String,
    pub parameters: GpayTokenParameters,
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GpayAllowedMethodsParameters {
    pub allowed_auth_methods: Vec<String>,
    pub allowed_card_networks: Vec<String>,
    pub assurance_details_required: Option<bool>,
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TrustpayGooglePayResponse {
    pub merchant_info: GooglePayMerchantInfo,
    pub allowed_payment_methods: Vec<GooglePayAllowedPaymentMethods>,
    pub transaction_info: GooglePayTransactionInfo,
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SdkSecretInfo {
    pub display: Secret<String>,
    pub payment: Secret<String>,
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TrustpayApplePayResponse {
    pub country_code: api_models::enums::CountryAlpha2,
    pub currency_code: api_models::enums::Currency,
    pub supported_networks: Vec<String>,
    pub merchant_capabilities: Vec<String>,
    pub total: ApplePayTotalInfo,
}

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplePayTotalInfo {
    pub label: String,
    pub amount: StringMajorUnit,
}

impl<F>
    TryFrom<
        ResponseRouterData<
            F,
            TrustpayCreateIntentResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
    > for RouterData<F, PaymentsPreProcessingData, PaymentsResponseData>
{
    type Error = Error;
    fn try_from(
        item: ResponseRouterData<
            F,
            TrustpayCreateIntentResponse,
            PaymentsPreProcessingData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let create_intent_response = item.response.init_result_data.to_owned();
        let secrets = item.response.secrets.to_owned();
        let instance_id = item.response.instance_id.to_owned();
        let pmt = PaymentsPreProcessingData::get_payment_method_type(&item.data.request)?;

        match (pmt, create_intent_response) {
            (
                enums::PaymentMethodType::ApplePay,
                InitResultData::AppleInitResultData(apple_pay_response),
            ) => get_apple_pay_session(instance_id, &secrets, apple_pay_response, item),
            (
                enums::PaymentMethodType::GooglePay,
                InitResultData::GoogleInitResultData(google_pay_response),
            ) => get_google_pay_session(instance_id, &secrets, google_pay_response, item),
            _ => Err(report!(errors::ConnectorError::InvalidWallet)),
        }
    }
}

pub(crate) fn get_apple_pay_session<F, T>(
    instance_id: String,
    secrets: &SdkSecretInfo,
    apple_pay_init_result: TrustpayApplePayResponse,
    item: ResponseRouterData<F, TrustpayCreateIntentResponse, T, PaymentsResponseData>,
) -> Result<RouterData<F, T, PaymentsResponseData>, error_stack::Report<errors::ConnectorError>> {
    Ok(RouterData {
        response: Ok(PaymentsResponseData::PreProcessingResponse {
            connector_metadata: None,
            pre_processing_id: PreprocessingResponseId::ConnectorTransactionId(instance_id),
            session_token: Some(SessionToken::ApplePay(Box::new(
                api_models::payments::ApplepaySessionTokenResponse {
                    session_token_data: Some(
                        api_models::payments::ApplePaySessionResponse::ThirdPartySdk(
                            api_models::payments::ThirdPartySdkSessionResponse {
                                secrets: secrets.to_owned().into(),
                            },
                        ),
                    ),
                    payment_request_data: Some(api_models::payments::ApplePayPaymentRequest {
                        country_code: apple_pay_init_result.country_code,
                        currency_code: apple_pay_init_result.currency_code,
                        supported_networks: Some(apple_pay_init_result.supported_networks.clone()),
                        merchant_capabilities: Some(
                            apple_pay_init_result.merchant_capabilities.clone(),
                        ),
                        total: apple_pay_init_result.total.into(),
                        merchant_identifier: None,
                        required_billing_contact_fields: None,
                        required_shipping_contact_fields: None,
                        recurring_payment_request: None,
                    }),
                    connector: "trustpay".to_string(),
                    delayed_session_token: true,
                    sdk_next_action: {
                        api_models::payments::SdkNextAction {
                            next_action: api_models::payments::NextActionCall::Sync,
                        }
                    },
                    connector_reference_id: None,
                    connector_sdk_public_key: None,
                    connector_merchant_id: None,
                },
            ))),
            connector_response_reference_id: None,
        }),
        // We don't get status from TrustPay but status should be AuthenticationPending by default for session response
        status: enums::AttemptStatus::AuthenticationPending,
        ..item.data
    })
}

pub(crate) fn get_google_pay_session<F, T>(
    instance_id: String,
    secrets: &SdkSecretInfo,
    google_pay_init_result: TrustpayGooglePayResponse,
    item: ResponseRouterData<F, TrustpayCreateIntentResponse, T, PaymentsResponseData>,
) -> Result<RouterData<F, T, PaymentsResponseData>, error_stack::Report<errors::ConnectorError>> {
    Ok(RouterData {
        response: Ok(PaymentsResponseData::PreProcessingResponse {
            connector_metadata: None,
            pre_processing_id: PreprocessingResponseId::ConnectorTransactionId(instance_id),
            session_token: Some(SessionToken::GooglePay(Box::new(
                api_models::payments::GpaySessionTokenResponse::GooglePaySession(
                    api_models::payments::GooglePaySessionResponse {
                        connector: "trustpay".to_string(),
                        delayed_session_token: true,
                        sdk_next_action: {
                            api_models::payments::SdkNextAction {
                                next_action: api_models::payments::NextActionCall::Sync,
                            }
                        },
                        merchant_info: google_pay_init_result.merchant_info.into(),
                        allowed_payment_methods: google_pay_init_result
                            .allowed_payment_methods
                            .into_iter()
                            .map(Into::into)
                            .collect(),
                        transaction_info: google_pay_init_result.transaction_info.into(),
                        secrets: Some((*secrets).clone().into()),
                        shipping_address_required: false,
                        email_required: false,
                        shipping_address_parameters:
                            api_models::payments::GpayShippingAddressParameters {
                                phone_number_required: false,
                            },
                    },
                ),
            ))),
            connector_response_reference_id: None,
        }),
        // We don't get status from TrustPay but status should be AuthenticationPending by default for session response
        status: enums::AttemptStatus::AuthenticationPending,
        ..item.data
    })
}

impl From<GooglePayTransactionInfo> for api_models::payments::GpayTransactionInfo {
    fn from(value: GooglePayTransactionInfo) -> Self {
        Self {
            country_code: value.country_code,
            currency_code: value.currency_code,
            total_price_status: value.total_price_status,
            total_price: value.total_price,
        }
    }
}

impl From<GooglePayMerchantInfo> for api_models::payments::GpayMerchantInfo {
    fn from(value: GooglePayMerchantInfo) -> Self {
        Self {
            merchant_id: Some(value.merchant_id.expose()),
            merchant_name: value.merchant_name.expose(),
        }
    }
}

impl From<GooglePayAllowedPaymentMethods> for api_models::payments::GpayAllowedPaymentMethods {
    fn from(value: GooglePayAllowedPaymentMethods) -> Self {
        Self {
            payment_method_type: value.payment_method_type,
            parameters: value.parameters.into(),
            tokenization_specification: value.tokenization_specification.into(),
        }
    }
}

impl From<GpayAllowedMethodsParameters> for api_models::payments::GpayAllowedMethodsParameters {
    fn from(value: GpayAllowedMethodsParameters) -> Self {
        Self {
            allowed_auth_methods: value.allowed_auth_methods,
            allowed_card_networks: value.allowed_card_networks,
            billing_address_required: None,
            billing_address_parameters: None,
            assurance_details_required: value.assurance_details_required,
        }
    }
}

impl From<GpayTokenizationSpecification> for api_models::payments::GpayTokenizationSpecification {
    fn from(value: GpayTokenizationSpecification) -> Self {
        Self {
            token_specification_type: value.token_specification_type,
            parameters: value.parameters.into(),
        }
    }
}

impl From<GpayTokenParameters> for api_models::payments::GpayTokenParameters {
    fn from(value: GpayTokenParameters) -> Self {
        Self {
            gateway: Some(value.gateway),
            gateway_merchant_id: Some(value.gateway_merchant_id.expose()),
            stripe_version: None,
            stripe_publishable_key: None,
            public_key: None,
            protocol_version: None,
        }
    }
}

impl From<SdkSecretInfo> for api_models::payments::SecretInfoToInitiateSdk {
    fn from(value: SdkSecretInfo) -> Self {
        Self {
            display: value.display,
            payment: value.payment,
        }
    }
}

impl From<ApplePayTotalInfo> for api_models::payments::AmountInfo {
    fn from(value: ApplePayTotalInfo) -> Self {
        Self {
            label: value.label,
            amount: value.amount,
            total_type: None,
        }
    }
}

#[derive(Default, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TrustpayRefundRequestCards {
    instance_id: String,
    amount: StringMajorUnit,
    currency: String,
    reference: String,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TrustpayRefundRequestBankRedirect {
    pub merchant_identification: MerchantIdentification,
    pub payment_information: BankPaymentInformation,
}

#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum TrustpayRefundRequest {
    CardsRefund(Box<TrustpayRefundRequestCards>),
    BankRedirectRefund(Box<TrustpayRefundRequestBankRedirect>),
}

impl<F> TryFrom<&TrustpayRouterData<&RefundsRouterData<F>>> for TrustpayRefundRequest {
    type Error = Error;
    fn try_from(item: &TrustpayRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let amount = item.amount.to_owned();
        match item.router_data.payment_method {
            enums::PaymentMethod::BankRedirect => {
                let auth = TrustpayAuthType::try_from(&item.router_data.connector_auth_type)
                    .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
                Ok(Self::BankRedirectRefund(Box::new(
                    TrustpayRefundRequestBankRedirect {
                        merchant_identification: MerchantIdentification {
                            project_id: auth.project_id,
                        },
                        payment_information: BankPaymentInformation {
                            amount: Amount {
                                amount,
                                currency: item.router_data.request.currency.to_string(),
                            },
                            references: References {
                                merchant_reference: item.router_data.request.refund_id.clone(),
                            },
                            debtor: None,
                        },
                    },
                )))
            }
            _ => Ok(Self::CardsRefund(Box::new(TrustpayRefundRequestCards {
                instance_id: item.router_data.request.connector_transaction_id.clone(),
                amount,
                currency: item.router_data.request.currency.to_string(),
                reference: item.router_data.request.refund_id.clone(),
            }))),
        }
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardsRefundResponse {
    pub status: i64,
    pub description: Option<String>,
    pub instance_id: String,
    pub payment_status: String,
    pub payment_description: Option<String>,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct BankRedirectRefundResponse {
    pub payment_request_id: i64,
    pub result_info: ResultInfo,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RefundResponse {
    CardsRefund(Box<CardsRefundResponse>),
    WebhookRefund(Box<WebhookPaymentInformation>),
    BankRedirectRefund(Box<BankRedirectRefundResponse>),
    BankRedirectRefundSyncResponse(Box<SyncResponseBankRedirect>),
    BankRedirectError(Box<ErrorResponseBankRedirect>),
}

fn handle_cards_refund_response(
    response: CardsRefundResponse,
    status_code: u16,
) -> CustomResult<(Option<ErrorResponse>, RefundsResponseData), errors::ConnectorError> {
    let (refund_status, msg) = get_refund_status(&response.payment_status)?;
    let error = if msg.is_some() {
        Some(ErrorResponse {
            code: response.payment_status,
            message: msg
                .clone()
                .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
            reason: msg,
            status_code,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    } else {
        None
    };
    let refund_response_data = RefundsResponseData {
        connector_refund_id: response.instance_id,
        refund_status,
    };
    Ok((error, refund_response_data))
}

fn handle_webhooks_refund_response(
    response: WebhookPaymentInformation,
    status_code: u16,
) -> CustomResult<(Option<ErrorResponse>, RefundsResponseData), errors::ConnectorError> {
    let refund_status = enums::RefundStatus::try_from(response.status)?;
    let error = if utils::is_refund_failure(refund_status) {
        let reason_info = response.status_reason_information.unwrap_or_default();
        Some(ErrorResponse {
            code: reason_info
                .reason
                .code
                .clone()
                .unwrap_or(consts::NO_ERROR_CODE.to_string()),
            // message vary for the same code, so relying on code alone as it is unique
            message: reason_info
                .reason
                .code
                .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
            reason: reason_info.reason.reject_reason,
            status_code,
            attempt_status: None,
            connector_transaction_id: response.references.payment_request_id.clone(),
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    } else {
        None
    };
    let refund_response_data = RefundsResponseData {
        connector_refund_id: response
            .references
            .payment_request_id
            .ok_or(errors::ConnectorError::MissingConnectorRefundID)?,
        refund_status,
    };
    Ok((error, refund_response_data))
}

fn handle_bank_redirects_refund_response(
    response: BankRedirectRefundResponse,
    status_code: u16,
) -> (Option<ErrorResponse>, RefundsResponseData) {
    let (refund_status, msg) = get_refund_status_from_result_info(response.result_info.result_code);
    let error = if msg.is_some() {
        Some(ErrorResponse {
            code: response.result_info.result_code.to_string(),
            // message vary for the same code, so relying on code alone as it is unique
            message: response.result_info.result_code.to_string(),
            reason: msg.map(|message| message.to_string()),
            status_code,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    } else {
        None
    };
    let refund_response_data = RefundsResponseData {
        connector_refund_id: response.payment_request_id.to_string(),
        refund_status,
    };
    (error, refund_response_data)
}

fn handle_bank_redirects_refund_sync_response(
    response: SyncResponseBankRedirect,
    status_code: u16,
) -> (Option<ErrorResponse>, RefundsResponseData) {
    let refund_status = enums::RefundStatus::from(response.payment_information.status);
    let error = if utils::is_refund_failure(refund_status) {
        let reason_info = response
            .payment_information
            .status_reason_information
            .unwrap_or_default();
        Some(ErrorResponse {
            code: reason_info
                .reason
                .code
                .clone()
                .unwrap_or(consts::NO_ERROR_CODE.to_string()),
            // message vary for the same code, so relying on code alone as it is unique
            message: reason_info
                .reason
                .code
                .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
            reason: reason_info.reason.reject_reason,
            status_code,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    } else {
        None
    };
    let refund_response_data = RefundsResponseData {
        connector_refund_id: response.payment_information.references.payment_request_id,
        refund_status,
    };
    (error, refund_response_data)
}

fn handle_bank_redirects_refund_sync_error_response(
    response: ErrorResponseBankRedirect,
    status_code: u16,
) -> (Option<ErrorResponse>, RefundsResponseData) {
    let error = Some(ErrorResponse {
        code: response.payment_result_info.result_code.to_string(),
        // message vary for the same code, so relying on code alone as it is unique
        message: response.payment_result_info.result_code.to_string(),
        reason: response.payment_result_info.additional_info,
        status_code,
        attempt_status: None,
        connector_transaction_id: None,
        network_advice_code: None,
        network_decline_code: None,
        network_error_message: None,
        connector_metadata: None,
    });
    //unreachable case as we are sending error as Some()
    let refund_response_data = RefundsResponseData {
        connector_refund_id: "".to_string(),
        refund_status: enums::RefundStatus::Failure,
    };
    (error, refund_response_data)
}

impl<F> TryFrom<RefundsResponseRouterData<F, RefundResponse>> for RefundsRouterData<F> {
    type Error = Error;
    fn try_from(item: RefundsResponseRouterData<F, RefundResponse>) -> Result<Self, Self::Error> {
        let (error, response) = match item.response {
            RefundResponse::CardsRefund(response) => {
                handle_cards_refund_response(*response, item.http_code)?
            }
            RefundResponse::WebhookRefund(response) => {
                handle_webhooks_refund_response(*response, item.http_code)?
            }
            RefundResponse::BankRedirectRefund(response) => {
                handle_bank_redirects_refund_response(*response, item.http_code)
            }
            RefundResponse::BankRedirectRefundSyncResponse(response) => {
                handle_bank_redirects_refund_sync_response(*response, item.http_code)
            }
            RefundResponse::BankRedirectError(response) => {
                handle_bank_redirects_refund_sync_error_response(*response, item.http_code)
            }
        };
        Ok(Self {
            response: error.map_or_else(|| Ok(response), Err),
            ..item.data
        })
    }
}

fn get_refund_status(
    payment_status: &str,
) -> CustomResult<(enums::RefundStatus, Option<String>), errors::ConnectorError> {
    let (is_failed, failure_message) = is_payment_failed(payment_status);
    if payment_status == "000.200.000" {
        Ok((enums::RefundStatus::Pending, None))
    } else if is_failed {
        Ok((
            enums::RefundStatus::Failure,
            Some(failure_message.to_string()),
        ))
    } else if is_payment_successful(payment_status)? {
        Ok((enums::RefundStatus::Success, None))
    } else {
        Ok((enums::RefundStatus::Pending, None))
    }
}

fn get_refund_status_from_result_info(
    result_code: i64,
) -> (enums::RefundStatus, Option<&'static str>) {
    match result_code {
        1001000 => (enums::RefundStatus::Success, None),
        1130001 => (enums::RefundStatus::Pending, Some("MapiPending")),
        1130000 => (enums::RefundStatus::Pending, Some("MapiSuccess")),
        1130004 => (enums::RefundStatus::Pending, Some("MapiProcessing")),
        1130002 => (enums::RefundStatus::Pending, Some("MapiAnnounced")),
        1130003 => (enums::RefundStatus::Pending, Some("MapiAuthorized")),
        1130005 => (enums::RefundStatus::Pending, Some("MapiAuthorizedOnly")),
        1112008 => (enums::RefundStatus::Failure, Some("InvalidPaymentState")),
        1112009 => (enums::RefundStatus::Failure, Some("RefundRejected")),
        1122006 => (
            enums::RefundStatus::Failure,
            Some("AccountCurrencyNotAllowed"),
        ),
        1132000 => (enums::RefundStatus::Failure, Some("InvalidMapiRequest")),
        1132001 => (enums::RefundStatus::Failure, Some("UnknownAccount")),
        1132002 => (
            enums::RefundStatus::Failure,
            Some("MerchantAccountDisabled"),
        ),
        1132003 => (enums::RefundStatus::Failure, Some("InvalidSign")),
        1132004 => (enums::RefundStatus::Failure, Some("DisposableBalance")),
        1132005 => (enums::RefundStatus::Failure, Some("TransactionNotFound")),
        1132006 => (enums::RefundStatus::Failure, Some("UnsupportedTransaction")),
        1132007 => (enums::RefundStatus::Failure, Some("GeneralMapiError")),
        1132008 => (
            enums::RefundStatus::Failure,
            Some("UnsupportedCurrencyConversion"),
        ),
        1132009 => (enums::RefundStatus::Failure, Some("UnknownMandate")),
        1132010 => (enums::RefundStatus::Failure, Some("CanceledMandate")),
        1132011 => (enums::RefundStatus::Failure, Some("MissingCid")),
        1132012 => (enums::RefundStatus::Failure, Some("MandateAlreadyPaid")),
        1132013 => (enums::RefundStatus::Failure, Some("AccountIsTesting")),
        1132014 => (enums::RefundStatus::Failure, Some("RequestThrottled")),
        1133000 => (enums::RefundStatus::Failure, Some("InvalidAuthentication")),
        1133001 => (enums::RefundStatus::Failure, Some("ServiceNotAllowed")),
        1133002 => (enums::RefundStatus::Failure, Some("PaymentRequestNotFound")),
        1133003 => (enums::RefundStatus::Failure, Some("UnexpectedGateway")),
        1133004 => (enums::RefundStatus::Failure, Some("MissingExternalId")),
        1152000 => (enums::RefundStatus::Failure, Some("RiskDecline")),
        _ => (enums::RefundStatus::Pending, None),
    }
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct TrustpayRedirectResponse {
    pub status: Option<String>,
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct Errors {
    pub code: i64,
    pub description: String,
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TrustpayErrorResponse {
    pub status: i64,
    pub description: Option<String>,
    pub errors: Option<Vec<Errors>>,
    pub instance_id: Option<String>,
    pub payment_description: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum CreditDebitIndicator {
    Crdt,
    Dbit,
}

#[derive(strum::Display, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum WebhookStatus {
    Paid,
    Rejected,
    Refunded,
    Chargebacked,
    #[serde(other)]
    Unknown,
}

impl TryFrom<WebhookStatus> for enums::AttemptStatus {
    type Error = errors::ConnectorError;
    fn try_from(item: WebhookStatus) -> Result<Self, Self::Error> {
        match item {
            WebhookStatus::Paid => Ok(Self::Charged),
            WebhookStatus::Rejected => Ok(Self::AuthorizationFailed),
            _ => Err(errors::ConnectorError::WebhookEventTypeNotFound),
        }
    }
}

impl TryFrom<WebhookStatus> for enums::RefundStatus {
    type Error = errors::ConnectorError;
    fn try_from(item: WebhookStatus) -> Result<Self, Self::Error> {
        match item {
            WebhookStatus::Paid => Ok(Self::Success),
            WebhookStatus::Refunded => Ok(Self::Success),
            WebhookStatus::Rejected => Ok(Self::Failure),
            _ => Err(errors::ConnectorError::WebhookEventTypeNotFound),
        }
    }
}

#[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "PascalCase")]
pub struct WebhookReferences {
    pub merchant_reference: Option<String>,
    pub payment_id: Option<String>,
    pub payment_request_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct WebhookAmount {
    pub amount: FloatMajorUnit,
    pub currency: enums::Currency,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]


OpenAPI Block Path: components.schemas.CustomerPaymentMethodsListResponse
{
  "type": "object",
  "required": [
    "customer_payment_methods"
  ],
  "properties": {
    "customer_payment_methods": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/CustomerPaymentMethod"
      },
      "description": "List of payment methods for customer"
    },
    "is_guest_customer": {
      "type": "boolean",
      "description": "Returns whether a customer id is not tied to a payment intent (only when the request is made against a client secret)",
      "nullable": true
    }
  }
}


pub fn add_browser_data(mut self, browser_info: BrowserInformation) -> Result<Self, Error> {
        self.browser_accept_header = Some(browser_info.get_accept_header()?);
        self.browser_user_agent = Some(browser_info.get_user_agent()?);
        self.browser_java_enabled = Some(browser_info.get_java_enabled()?);
        self.browser_javascript_enabled = browser_info.get_java_script_enabled().ok();
        self.browser_language = Some(browser_info.get_language()?);
        self.browser_color_depth = Some(browser_info.get_color_depth()?.to_string());
        self.browser_screen_height = Some(browser_info.get_screen_height()?.to_string());
        self.browser_screen_width = Some(browser_info.get_screen_width()?.to_string());
        self.browser_t_z = Some(browser_info.get_time_zone()?.to_string());
        self.browser_i_p = Some(browser_info.get_ip_address()?);
        Ok(self)
    }


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


impl api::PaymentToken for Paysafe {}


pub struct Sale {
    #[serde(rename = "@id")]
    pub id: String,
    #[serde(rename = "@reportGroup")]
    pub report_group: String,
    #[serde(rename = "@customerId", skip_serializing_if = "Option::is_none")]
    pub customer_id: Option<String>,
    pub order_id: String,
    pub amount: MinorUnit,
    pub order_source: OrderSource,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bill_to_address: Option<VantivAddressData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ship_to_address: Option<VantivAddressData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub card: Option<WorldpayvantivCardData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub token: Option<TokenizationData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub enhanced_data: Option<EnhancedData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub processing_type: Option<VantivProcessingType>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub original_network_transaction_id: Option<Secret<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub allow_partial_auth: Option<bool>,
}


pub async fn kv_wrapper<'a, T, D, S>(
    store: &KVRouterStore<D>,
    op: KvOperation<'a, S>,
    partition_key: PartitionKey<'a>,
) -> CustomResult<KvResult<T>, RedisError>
where
    T: de::DeserializeOwned,
    D: crate::database::store::DatabaseStore,
    S: serde::Serialize + Debug + KvStorePartition + UniqueConstraints + Sync,
{
    let redis_conn = store.get_redis_conn()?;

    let key = format!("{partition_key}");

    let type_name = std::any::type_name::<T>();
    let operation = op.to_string();

    let ttl = store.ttl_for_kv;

    let result = async {
        match op {
            KvOperation::Hset(value, sql) => {
                logger::debug!(kv_operation= %operation, value = ?value);

                redis_conn
                    .set_hash_fields(&key.into(), value, Some(ttl.into()))
                    .await?;

                store
                    .push_to_drainer_stream::<S>(sql, partition_key)
                    .await?;

                Ok(KvResult::Hset(()))
            }

            KvOperation::HGet(field) => {
                let result = redis_conn
                    .get_hash_field_and_deserialize(&key.into(), field, type_name)
                    .await?;
                Ok(KvResult::HGet(result))
            }

            KvOperation::Scan(pattern) => {
                let result: Vec<T> = redis_conn
                    .hscan_and_deserialize(&key.into(), pattern, None)
                    .await
                    .and_then(|result| {
                        if result.is_empty() {
                            Err(report!(RedisError::NotFound))
                        } else {
                            Ok(result)
                        }
                    })?;
                Ok(KvResult::Scan(result))
            }

            KvOperation::HSetNx(field, value, sql) => {
                logger::debug!(kv_operation= %operation, value = ?value);

                value.check_for_constraints(&redis_conn).await?;

                let result = redis_conn
                    .serialize_and_set_hash_field_if_not_exist(&key.into(), field, value, Some(ttl))
                    .await?;

                if matches!(result, redis_interface::HsetnxReply::KeySet) {
                    store
                        .push_to_drainer_stream::<S>(sql, partition_key)
                        .await?;
                    Ok(KvResult::HSetNx(result))
                } else {
                    Err(report!(RedisError::SetNxFailed))
                }
            }

            KvOperation::SetNx(value, sql) => {
                logger::debug!(kv_operation= %operation, value = ?value);

                let result = redis_conn
                    .serialize_and_set_key_if_not_exist(&key.into(), value, Some(ttl.into()))
                    .await?;

                value.check_for_constraints(&redis_conn).await?;

                if matches!(result, redis_interface::SetnxReply::KeySet) {
                    store
                        .push_to_drainer_stream::<S>(sql, partition_key)
                        .await?;
                    Ok(KvResult::SetNx(result))
                } else {
                    Err(report!(RedisError::SetNxFailed))
                }
            }

            KvOperation::Get => {
                let result = redis_conn
                    .get_and_deserialize_key(&key.into(), type_name)
                    .await?;
                Ok(KvResult::Get(result))
            }
        }
    };

    let attributes = router_env::metric_attributes!(("operation", operation.clone()));
    result
        .await
        .inspect(|_| {
            logger::debug!(kv_operation= %operation, status="success");
            metrics::KV_OPERATION_SUCCESSFUL.add(1, attributes);
        })
        .inspect_err(|err| {
            logger::error!(kv_operation = %operation, status="error", error = ?err);
            metrics::KV_OPERATION_FAILED.add(1, attributes);
        })
}


impl webhooks::IncomingWebhook for Facilitapay {
    async fn verify_webhook_source(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: crypto::Encryptable<masking::Secret<serde_json::Value>>,
        _connector_name: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        let webhook_body: responses::FacilitapayWebhookNotification = request
            .body
            .parse_struct("FacilitapayWebhookNotification")
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;

        let connector_webhook_secrets = match connector_webhook_details {
            Some(secret_value) => {
                let secret = secret_value
                    .parse_value::<api_models::admin::MerchantConnectorWebhookDetails>(
                        "MerchantConnectorWebhookDetails",
                    )
                    .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
                secret.merchant_secret.expose()
            }
            None => "default_secret".to_string(),
        };

        // FacilitaPay uses a simple 4-digit secret for verification
        Ok(webhook_body.notification.secret.peek() == &connector_webhook_secrets)
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let webhook_body: responses::FacilitapayWebhookNotification = request
            .body
            .parse_struct("FacilitapayWebhookNotification")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;

        // Extract transaction ID from the webhook data
        let transaction_id = match &webhook_body.notification.data {
            responses::FacilitapayWebhookData::Transaction { transaction_id }
            | responses::FacilitapayWebhookData::CardPayment { transaction_id, .. } => {
                transaction_id.clone()
            }
            responses::FacilitapayWebhookData::Exchange {
                transaction_ids, ..
            }
            | responses::FacilitapayWebhookData::Wire {
                transaction_ids, ..
            }
            | responses::FacilitapayWebhookData::WireError {
                transaction_ids, ..
            } => transaction_ids
                .first()
                .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?
                .clone(),
        };

        // For refund webhooks, Facilitapay sends the original payment transaction ID
        // not the refund transaction ID
        Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
            api_models::payments::PaymentIdType::ConnectorTransactionId(transaction_id),
        ))
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let webhook_body: responses::FacilitapayWebhookNotification = request
            .body
            .parse_struct("FacilitapayWebhookNotification")
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;

        // Note: For "identified" events, we need additional logic to determine if it's cross-currency
        // Since we don't have access to the payment data here, we'll default to Success for now
        // The actual status determination happens in the webhook processing flow
        let event = match webhook_body.notification.event_type {
            FacilitapayWebhookEventType::ExchangeCreated => {
                api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing
            }
            FacilitapayWebhookEventType::Identified
            | FacilitapayWebhookEventType::PaymentApproved
            | FacilitapayWebhookEventType::WireCreated => {
                api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess
            }
            FacilitapayWebhookEventType::PaymentExpired
            | FacilitapayWebhookEventType::PaymentFailed => {
                api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure
            }
            FacilitapayWebhookEventType::PaymentRefunded => {
                api_models::webhooks::IncomingWebhookEvent::RefundSuccess
            }
            FacilitapayWebhookEventType::WireWaitingCorrection => {
                api_models::webhooks::IncomingWebhookEvent::PaymentActionRequired
            }
        };

        Ok(event)
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let webhook_body: responses::FacilitapayWebhookNotification = request
            .body
            .parse_struct("FacilitapayWebhookNotification")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        Ok(Box::new(webhook_body))
    }
}

impl webhooks::IncomingWebhook for for Facilitapay


impl TraceUrlAssert {
    fn compare_url(&self, url: &str) -> bool {
        match self {
            Self::Match(value) => url == value,
            Self::EndsWith(end) => url.ends_with(end),
        }
    }
}


pub fn get_id(&self) -> &id_type::PaymentId {
        self.payment_id
    }


pub async fn validate_card_testing_guard_checks(
    state: &SessionState,
    request: &api::PaymentsRequest,
    payment_method_data: Option<&api_models::payments::PaymentMethodData>,
    customer_id: &Option<common_utils::id_type::CustomerId>,
    business_profile: &domain::Profile,
) -> RouterResult<Option<CardTestingGuardData>> {
    match &business_profile.card_testing_guard_config {
        Some(card_testing_guard_config) => {
            let fingerprint = generate_fingerprint(payment_method_data, business_profile).await?;

            let card_testing_guard_expiry = card_testing_guard_config.card_testing_guard_expiry;

            let mut card_ip_blocking_cache_key = String::new();
            let mut guest_user_card_blocking_cache_key = String::new();
            let mut customer_id_blocking_cache_key = String::new();

            if card_testing_guard_config.is_card_ip_blocking_enabled {
                if let Some(browser_info) = &request.browser_info {
                    #[cfg(feature = "v1")]
                    {
                        let browser_info =
                            serde_json::from_value::<BrowserInformation>(browser_info.clone())
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable("could not parse browser_info")?;

                        if let Some(browser_info_ip) = browser_info.ip_address {
                            card_ip_blocking_cache_key =
                                helpers::validate_card_ip_blocking_for_business_profile(
                                    state,
                                    browser_info_ip,
                                    fingerprint.clone(),
                                    card_testing_guard_config,
                                )
                                .await?;
                        }
                    }

                    #[cfg(feature = "v2")]
                    {
                        if let Some(browser_info_ip) = browser_info.ip_address {
                            card_ip_blocking_cache_key =
                                helpers::validate_card_ip_blocking_for_business_profile(
                                    state,
                                    browser_info_ip,
                                    fingerprint.clone(),
                                    card_testing_guard_config,
                                )
                                .await?;
                        }
                    }
                }
            }

            if card_testing_guard_config.is_guest_user_card_blocking_enabled {
                guest_user_card_blocking_cache_key =
                    helpers::validate_guest_user_card_blocking_for_business_profile(
                        state,
                        fingerprint.clone(),
                        customer_id.clone(),
                        card_testing_guard_config,
                    )
                    .await?;
            }

            if card_testing_guard_config.is_customer_id_blocking_enabled {
                if let Some(customer_id) = customer_id.clone() {
                    customer_id_blocking_cache_key =
                        helpers::validate_customer_id_blocking_for_business_profile(
                            state,
                            customer_id.clone(),
                            business_profile.get_id(),
                            card_testing_guard_config,
                        )
                        .await?;
                }
            }

            Ok(Some(CardTestingGuardData {
                is_card_ip_blocking_enabled: card_testing_guard_config.is_card_ip_blocking_enabled,
                card_ip_blocking_cache_key,
                is_guest_user_card_blocking_enabled: card_testing_guard_config
                    .is_guest_user_card_blocking_enabled,
                guest_user_card_blocking_cache_key,
                is_customer_id_blocking_enabled: card_testing_guard_config
                    .is_customer_id_blocking_enabled,
                customer_id_blocking_cache_key,
                card_testing_guard_expiry,
            }))
        }
        None => Ok(None),
    }
}


pub struct CheckbookPaymentsRequest {
    name: Secret<String>,
    recipient: pii::Email,
    amount: FloatMajorUnit,
    description: String,
}


### Refactors

* **api_models:**  enhance accepted countries/currencies types (#807) (f9ef3135)
* **services:**   make AppState impl generic using AppStateInfo (#805) (642c3f3a)

- - -

## 0.5.2 (2023-03-24)

### Chores

*  prepare for building production Docker images (#794) (6ddc30eb)

### Bug Fixes

* **connector:**  [Airwallex] Change Session Token to Init Payment (#798) (a3c00339)

### Other Changes

* **router:**  change MAX_ID_LENGTH to 64 (#792) (346bd954)

### Refactors

*  extract kms module to `external_services` crate (#793) (029e3894)

- - -

## 0.5.1 (2023-03-21)

### Documentation Changes

* **try_local_system:**
  *  add Ubuntu on WSL2 setup instructions (#767) (1d2166cf)
  *  add API key creation step (#765) (4b268068)

### New Features

* **pm_list:**  handle client secret check (#759) (82344fc4)
*  add in-memory cache support for config table (#751) (abedaae4)
*  compile time optimization (#775) (5b5557b7)
* **router:**
  *  add support for stateful straight through routing (#752) (568bf01a)
  *  adding metrics for tracking behavior throughout the `router` crate  (#768) (d302b286)
* **router_env:**
  *  making metric flow as a trait for extensibility (#797) (df699e2b)
* **core:**  accept gateway credentials in the request body in payments and refunds (#766) (cb188f92)
* **connector:**
  *  Add support to provide connector_payment_meta for capture and void request (#770) (6c008ae6)
  *  [Trustpay] add webhooks (payment and refund events) (#746) (853dfa16)

### Bug Fixes

*  process delete response from basilisk-v3 as plaintext instead of JWE (#791) (699ca4f)
* **storage:**  add serialization for primitivedatetime for diesel structs (#764) (f27732a6)

### Refactors

*  get connection pool based on olap/oltp features (#743) (a392fb16)

- - -

## 0.5.0 (2023-03-21)

### Build System / Dependencies

* **deps:**  update deps (#734) (16bc886c)

### Chores

* **merchant_account:**  remove `api_key` field (#713) (230fcdd4)
* **config:**  move connector base URLs under the `[connectors]` table (#723) (df8c8b5a)
*  address Rust 1.68 clippy lints (#728) (1ffabb40)

### Continuous Integration

* **release:**  specify `fetch-depth` for code checkout and use official Docker GitHub actions (#722) (c451368f)

### Documentation Changes

*  Update naming conventions and added examples (#709) (98415193)
* **openapi:**  document path parameters for API keys endpoints (#702) (9062dc80)

### New Features

* **connector:**
  *  [Mollie]: add authorize, void, refund, psync, rsync support for mollie connector (#740) (168fa32)
  *  [worldline] add webhook support for connector (#721) (13a8ce8e)
  *  [Trustpay] add authorize (cards 3ds, no3ds and bank redirects), refund, psync, rsync (#717) (e102cae7)
  *  [Fiserv] add Refunds, Cancel and Wallets flow along with Unit Tests (#593) (cd1c5409)
  *  Add support for complete authorize payment after 3DS redirection (#741) (ec2b1b18)
*  removing unnecessary logs from console (#753) (1021d1ae)
*  Time based deletion of temp card (#729) (db3d3164)
*  populate fields from payment attempt in payment list (#736) (b5b3d57c)
*  add generic in-memory cache interface (#737) (7f5e5d86)
*  Add HSTS headers to response (#725) (7ed665ec)
*  cache reverse lookup fetches on redis (#719) (1a27faca)
* **compatibility:**  add webhook support for stripe compatibility (#710) (79160504)

### Bug Fixes

* **docker-compose:**  remove port for hyperswitch-server-init in docker-compose.yml (#763) (20b93276)
*  fixing docker compose setup & adding redisinsight (#748) (5c9bec9f)
* **kms:**  log KMS SDK errors using the `Debug` impl (#720) (468aa87f)
* **errors:**
  *  Replace PaymentMethod with PaymentModethodData in test.rs (#716) (763ee094)
  *  use `Debug` impl instead of `Display` for error types wrapping `error_stack::Report` (#714) (45484752)

### Other Changes

*  card_fingerprint not sent by basilisk_hs (#754) (5ae2f63f)

### Refactors

* **merchant_account:**  add back `api_key` field for backward compatibility (#761) (661dd48a)
* **connector:**  update add_connector script (#762) (78794ed6)
* **metrics:**  use macros for constructing counter and histogram metrics (#755) (58106d91)
* **kms:**  share a KMS client for all KMS operations (#744) (a3ff2e8d)
*  Basilisk hs integration (#704) (585618e5)
*  Add service_name to get and delete request (#738) (8b7ae9c3)
*  Add secret to metadata (#706) (d36afbed)
* **client:**
  *  simplify HTTP client construction (#731) (1756d1c4)
  *  remove dependence on `ROUTER_HTTP_PROXY` and `ROUTER_HTTPS_PROXY` env vars (#730) (c085e460)
* **authentication:**  authenticate merchant by API keys from API keys table (#712) (afd08d42)
* **api_keys:**  use a KMS encrypted API key hashing key and remove key ID prefix from plaintext API keys (#639) (3a3b33ac)

### Tests

* **masking:**  add suitable feature gates for basic tests (#745) (4859b6e4)

- - -

## 0.3.0 (2023-03-05)

### Chores
* **connectors:**  log connector request and response at debug level (#624) (6a487b19)

### Continuous Integration

* **workflow:** adding build only sandbox feature to reduce build time (#664) (d1c9305e)
* **workflow:** run cargo hack only for code changes (#663) (f931c427)

### Documentation Changes

* **openapi:**  document security schemes (#676) (c5fda7ac)

### New Features

* **session_token:**  create session token only if pmt is enabled (#703) (e1afeb64)
* **router:**
  *  serve OpenAPI docs at `/docs` (#698) (ed2907e1)
  *  added incoming refund webhooks flow (#683) (f12abbce)
* **list:**  global filter mapping for payment methods via card network (#694) (adca6bca)
*  store card network for cards (#687) (bfca26d9)
*  add support for `ANG` currency (#681) (03096eff)
*  Add bank redirect mapping to adyen and stripe (#680) (e6f627d9)
*  api contract change for wallet (#628) (ff86417e)
*  Add support for a redis pubsub interface (#614) (aaf37250)
*  initial `nix` setup using `cargo2nix` (#599) (73d0538d)
* **connector:**
  *  [Bambora] Add support for cards Authorize, psync, capture, void, refund, Rsync (#677) (0de5d441)
  *  [MultiSafePay] Add support for cards Authorize, psync, capture, void, refund, Rsync  (#658) (79aa8f3d)
  *  [Dlocal] Add support for authorize, capture, void, refund, psync, rsync (#650) (7792de55)
* **pm_list:**  support for sending bank names (#678) (576f8e1f)
* **card_network:**  add additional enum variants in card network (#671) (db8bc164)
* **stripe:**
  *  eps, giropay and ideal using stripe (#529) (028e1401)
  *  get error message for failed redirect payments (#615) (12f25f05)

### Bug Fixes

*  Populate amount_captured in case of success (#700) (d622b743)
*  Error Mapping for Bluensap & Card Number for Airwallex (#686) (35a74baf)
*  add currency in verify request data (#619) (32de632d)
*  add zero-padded formatting for error code (#627) (63f9b612)
*  check if bank_pm exists and then send request (#679) (76a9b557)
* **connector:**
  *  convert cents to dollar before sending to connector (#699) (3e883192)
  *  fix wordline card number validation issue (#695) (1a875348)
  *  fix wordline tests and visa card issuer support (#688) (d0c9dded)
* **adyen:**  adyen psync fail fix (#691) (2e99152d)
* **customer:**  populate email from customer table if not present in request (#692) (cf71d7aa)
* **list:**
  *  remove enabled payment methods from list customer payment method (#689) (5c29f37a)
  *  fix card network filtering (#684) (718c8a42)
  *  adding config changes for filtering `pm` based on countries & currencies (#669) (060c5419)
* **compatibility:**
  *  change next_action type and customer request type (#675) (7f22c22c)
  *  map stripe country_code to payment_request country code (#667) (7044b80b)
* **core:**  send metadata in payments response (#670) (b80f19e2)
* **router:**  allow setup future usage to be updated in payment update and confirm requests (#610) (7fd82211)

### Other Changes

* **stripe:**  send statement descriptor to stripe (#707) (641c4d6d)
*  use connector error handler for 500 error messages. (#696) (9fe20932)
*  populate failed status and add bank_redirect (#674)
* **refunds:**  skip validate refunds for card (#672) (5cdbef04)
* **router/webhooks:**  expose additional incoming request details to webhooks flow (#637) (1b3b7f5b)
* **braintree:**  create basic auth for braintree (#602) (c47619b5)

### Refactors

*  add better log to parse struct (#621) (275155a8)
*  Pass country and currency as json format in MCA (#523) (d27e6be5)
*  use simple uuid instead of hyphens (#605) (c467a47a)
*  add payment_issuer and payment_experience in pa (#491) (66563595)
* **router:**  remove foreign wrapper type (#616) (7bd2008a)
* **core:**
  *  add payment method list route to payment_methods (#682) (5449ce46)
  *  make attempt id as mandatory in router_data (#604) (626e467e)
* **pm_list:**
  *  pm_list for bank redirects (#685) (2701cceb)
  *  modify pm list to support new api contract (#657) (a2616d87)
* **connector:**  remove `peek()` on PII info (#642) (46f77d07)
* **connector-template:**  raise errors instead of using `todo!()` (#620) (b1a6be5a)
* **redirection:**  `From` impl for redirection data for ease of use (#613) (e8255b4a)

- - -

## 0.3.0 (2023-02-25)

### Build System / Dependencies

* **docker-compose:**  increase docker health check interval for hyperswitch-server (#534)

### Chores

* **release:**  port release bug fixes to main branch (#612) (a8d6ce83)

### Continuous Integration

*  run CI checks on merge queue events (#530) (c7b9e9c1)

### Documentation Changes

* **add_connector:**  fix typo (#584) (a4f3abf3)

### New Features

* **router:**
  *  include eligible connectors list in list payment methods (#644) (92771b3b)
  *  API endpoints for managing API keys (#511) (1bdc8955)
* **connector:**
  *  [Airwallex] add authorize, capture, void, psync, Webhooks support (#646) (6a67dd8b)
  *  [Bluesnap] add authorize, capture, void, refund, psync, rsync and Webhooks support (#649) (7efdc3c5)
  *  add authorize, capture, void, refund, psync support for Nuvei (#645) (03a9f5a9)
*  Added applepay feature (#636) (1e84c07c)
*  add `track_caller` to functions that perform `change_context` (#592) (8d2e573a)
* Redis cache for MCA fetch and update (#515) (963cb528)
* **api_models:**  add error structs (#532) (d107b44f)

### Bug Fixes

* **connector:**  update Bluesnap in routable connectors  (#654) (64cb2ffc)
*  allow errors with status code 200 to pass (#601) (8a8767e9)
*  don't call connector if connector transaction id doesn't exist (#525) (326d6beb)
*  throw 500 error when redis goes down (#531) (aafb115a)
* **router:**
  *  allow setup future usage to be updated in payment update and confirm requests (#610) (#638) (6c128f82)
  *  feature gate openssl deps for basilisk feature (#536) (e4956820)
* **checkout:**  Error Response when wrong api key is passed (#596) (55b6d88a)
* **core:**  use guard for access token result (#522) (903b4521)

### Other Changes

* **router:**
  *  webhooks enhancement (#637) (#641) (3bc9feb0)
  *  api keys path params (#609) (effa7a00)

### Refactors

* **router:**
  *  update payments api contract to accept a list of connectors (#643) (8f1f626c)
  *  api-key routes refactoring (#600) (e6408276)
  *  appstate as trait in authentication (#588) (eaf98e66)
* **compatibility:**  add additional fields to stripe payment and refund response types (#618) (2ea09e34)
*  Throw 500 error on database connection error instead of panic (#527) (f1e3bf48)
*  send full payment object for payment sync (#526) (6c2a1fea)
* **middleware:**  change visibility to `pub` (#587) (4884a24d)

- - -

## 0.2.1 (2023-02-17)

### Fixes
- fix payment_status not updated when adding payment method ([#446])
- Decide connector only when the payment method is confirm ([10ea4919ba07d3198a6bbe3f3d4d817a23605924](https://github.com/juspay/hyperswitch/commit/10ea4919ba07d3198a6bbe3f3d4d817a23605924))
- Fix panics caused with empty diesel updates ([448595498114cd15158b4a78fc32d8e6dc1b67ee](https://github.com/juspay/hyperswitch/commit/448595498114cd15158b4a78fc32d8e6dc1b67ee))

- - -

## 0.2.0 (2023-01-23) - Initial Release

### Supported Connectors

- [ACI](https://www.aciworldwide.com/)
- [Adyen](https://www.adyen.com/)
- [Authorize.net](https://www.authorize.net/)
- [Braintree](https://www.braintreepayments.com/)
- [Checkout.com](https://www.checkout.com/)
- [Cybersource](https://www.cybersource.com)
- [Fiserv](https://www.fiserv.com/)
- [Global Payments](https://www.globalpayments.com)
- [Klarna](https://www.klarna.com/)
- [PayU](https://payu.in/)
- [Rapyd](https://www.rapyd.net/)
- [Shift4](https://www.shift4.com/)
- [Stripe](https://stripe.com/)
- [Wordline](https://worldline.com/)

### Supported Payment Methods

- Cards No 3DS
- Cards 3DS*
- [Apple Pay](https://www.apple.com/apple-pay/)*
- [Google Pay](https://pay.google.com)*
- [Klarna](https://www.klarna.com/)*
- [PayPal](https://www.paypal.com/)*

### Supported Payment Functionalities

- Payments (Authorize/Sync/Capture/Cancel)
- Refunds (Execute/Sync)
- Saved Cards
- Mandates (No 3DS)*
- Customers
- Merchants
- ConnectorAccounts

\* May not be supported on all connectors


File: crates/analytics/src/sdk_events/metrics/payment_attempts.rs

use std::collections::HashSet;

use api_models::analytics::{
    sdk_events::{
        SdkEventDimensions, SdkEventFilters, SdkEventMetricsBucketIdentifier, SdkEventNames,
    },
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

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

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

#[async_trait::async_trait]
impl<T> super::SdkEventMetric<T> for PaymentAttempts
where
    T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
    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: &[SdkEventDimensions],
        publishable_key: &str,
        filters: &SdkEventFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>> {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::SdkEventsAnalytics);
        let dimensions = dimensions.to_vec();

        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()?;

        if let Some(granularity) = granularity {
            query_builder
                .add_granularity_in_mins(granularity)
                .switch()?;
        }

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

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

        query_builder
            .add_bool_filter_clause("first_event", 1)
            .switch()?;

        query_builder
            .add_filter_clause("event_name", SdkEventNames::PaymentAttempt)
            .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.as_ref() {
            query_builder
                .add_group_by_clause("time_bucket")
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .execute_query::<SdkEventMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    SdkEventMetricsBucketIdentifier::new(
                        i.payment_method.clone(),
                        i.platform.clone(),
                        i.browser_name.clone(),
                        i.source.clone(),
                        i.component.clone(),
                        i.payment_experience.clone(),
                        i.time_bucket.clone(),
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


OpenAPI Block Path: components.schemas.BoletoVoucherData
{
  "type": "object",
  "properties": {
    "social_security_number": {
      "type": "string",
      "description": "The shopper's social security number (CPF or CNPJ)",
      "nullable": true
    },
    "bank_number": {
      "type": "string",
      "description": "The shopper's bank account number associated with the boleto",
      "nullable": true
    },
    "document_type": {
      "allOf": [
        {
          "$ref": "#/components/schemas/DocumentKind"
        }
      ],
      "default": "Cnpj",
      "nullable": true
    },
    "fine_percentage": {
      "type": "string",
      "description": "The fine percentage charged if payment is overdue",
      "nullable": true
    },
    "fine_quantity_days": {
      "type": "string",
      "description": "The number of days after the due date when the fine is applied",
      "nullable": true
    },
    "interest_percentage": {
      "type": "string",
      "description": "The interest percentage charged on late payments",
      "nullable": true
    },
    "write_off_quantity_days": {
      "type": "string",
      "description": "The number of days after which the boleto is written off (canceled)",
      "nullable": true
    },
    "messages": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Custom messages or instructions to display on the boleto",
      "nullable": true
    },
    "due_date": {
      "type": "string",
      "format": "date",
      "example": "2025-08-22",
      "nullable": true
    }
  }
}


impl SubscriptionNew {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        subscription_id: String,
        status: String,
        billing_processor: Option<String>,
        payment_method_id: Option<String>,
        merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
        client_secret: Option<String>,
        connector_subscription_id: Option<String>,
        merchant_id: common_utils::id_type::MerchantId,
        customer_id: common_utils::id_type::CustomerId,
        metadata: Option<SecretSerdeValue>,
        profile_id: common_utils::id_type::ProfileId,
    ) -> Self {
        let now = common_utils::date_time::now();
        Self {
            subscription_id,
            status,
            billing_processor,
            payment_method_id,
            merchant_connector_id,
            client_secret,
            connector_subscription_id,
            merchant_id,
            customer_id,
            metadata,
            created_at: now,
            modified_at: now,
            profile_id,
        }
    }
}


pub async fn get_network_token(
    state: &routes::SessionState,
    customer_id: id_type::CustomerId,
    network_token_requestor_ref_id: String,
    tokenization_service: &settings::NetworkTokenizationService,
) -> CustomResult<pm_types::TokenResponse, errors::NetworkTokenizationError> {
    let mut request = services::Request::new(
        services::Method::Post,
        tokenization_service.fetch_token_url.as_str(),
    );
    let payload = pm_types::GetCardToken {
        card_reference: network_token_requestor_ref_id,
        customer_id,
    };

    request.add_header(headers::CONTENT_TYPE, "application/json".into());
    request.add_header(
        headers::AUTHORIZATION,
        tokenization_service
            .token_service_api_key
            .clone()
            .peek()
            .clone()
            .into_masked(),
    );
    request.add_default_headers();
    request.set_body(RequestContent::Json(Box::new(payload)));

    logger::info!("Request to fetch network token: {:?}", request);

    // Send the request using `call_connector_api`
    let response = services::call_connector_api(state, request, "get network token")
        .await
        .change_context(errors::NetworkTokenizationError::ApiError);

    let res = response
        .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)
        .attach_printable("Error while receiving response")
        .and_then(|inner| match inner {
            Err(err_res) => {
                let parsed_error: pm_types::NetworkTokenErrorResponse = err_res
                    .response
                    .parse_struct("Card Network Tokenization Response")
                    .change_context(
                        errors::NetworkTokenizationError::ResponseDeserializationFailed,
                    )?;
                logger::error!(
                    error_code = %parsed_error.error_info.code,
                    developer_message = %parsed_error.error_info.developer_message,
                    "Network tokenization error: {}",
                    parsed_error.error_message
                );
                Err(errors::NetworkTokenizationError::ResponseDeserializationFailed)
                    .attach_printable(format!("Response Deserialization Failed: {err_res:?}"))
            }
            Ok(res) => Ok(res),
        })?;

    let token_response: pm_types::TokenResponse = res
        .response
        .parse_struct("Get Network Token Response")
        .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
    logger::info!("Fetch Network Token Response: {:?}", token_response);

    Ok(token_response)
}


pub fn merge_transaction_responses(
        auth_response: &Self,
        capture_response: &Self,
    ) -> Result<Self, error_stack::Report<ApiErrorResponse>> {
        match (auth_response, capture_response) {
            (
                Self::TransactionResponse {
                    resource_id: _,
                    redirection_data: auth_redirection_data,
                    mandate_reference: auth_mandate_reference,
                    connector_metadata: auth_connector_metadata,
                    network_txn_id: auth_network_txn_id,
                    connector_response_reference_id: auth_connector_response_reference_id,
                    incremental_authorization_allowed: auth_incremental_auth_allowed,
                    charges: auth_charges,
                },
                Self::TransactionResponse {
                    resource_id: capture_resource_id,
                    redirection_data: capture_redirection_data,
                    mandate_reference: capture_mandate_reference,
                    connector_metadata: capture_connector_metadata,
                    network_txn_id: capture_network_txn_id,
                    connector_response_reference_id: capture_connector_response_reference_id,
                    incremental_authorization_allowed: capture_incremental_auth_allowed,
                    charges: capture_charges,
                },
            ) => Ok(Self::TransactionResponse {
                resource_id: capture_resource_id.clone(),
                redirection_data: Box::new(
                    capture_redirection_data
                        .clone()
                        .or_else(|| *auth_redirection_data.clone()),
                ),
                mandate_reference: Box::new(
                    auth_mandate_reference
                        .clone()
                        .or_else(|| *capture_mandate_reference.clone()),
                ),
                connector_metadata: capture_connector_metadata
                    .clone()
                    .or(auth_connector_metadata.clone()),
                network_txn_id: capture_network_txn_id
                    .clone()
                    .or(auth_network_txn_id.clone()),
                connector_response_reference_id: capture_connector_response_reference_id
                    .clone()
                    .or(auth_connector_response_reference_id.clone()),
                incremental_authorization_allowed: (*capture_incremental_auth_allowed)
                    .or(*auth_incremental_auth_allowed),
                charges: auth_charges.clone().or(capture_charges.clone()),
            }),
            _ => Err(ApiErrorResponse::NotSupported {
                message: "Invalid Flow ".to_owned(),
            }
            .into()),
        }
    }


File: crates/router/src/core.rs

pub mod admin;
pub mod api_keys;
pub mod api_locking;
#[cfg(feature = "v1")]
pub mod apple_pay_certificates_migration;
pub mod authentication;
#[cfg(feature = "v1")]
pub mod blocklist;
pub mod cache;
pub mod card_testing_guard;
pub mod cards_info;
pub mod conditional_config;
pub mod configs;
#[cfg(feature = "olap")]
pub mod connector_onboarding;
pub mod connector_validation;
#[cfg(any(feature = "olap", feature = "oltp"))]
pub mod currency;
pub mod customers;
pub mod disputes;
pub mod encryption;
pub mod errors;
pub mod external_service_auth;
pub mod files;
#[cfg(feature = "frm")]
pub mod fraud_check;
pub mod gsm;
pub mod health_check;
#[cfg(feature = "v1")]
pub mod locker_migration;
pub mod mandate;
pub mod metrics;
pub mod payment_link;
pub mod payment_methods;
pub mod payments;
#[cfg(feature = "payouts")]
pub mod payout_link;
#[cfg(feature = "payouts")]
pub mod payouts;
pub mod pm_auth;
pub mod poll;
#[cfg(feature = "recon")]
pub mod recon;
#[cfg(feature = "v1")]
pub mod refunds;
#[cfg(feature = "v2")]
pub mod refunds_v2;

#[cfg(feature = "v1")]
pub mod debit_routing;
pub mod routing;
pub mod surcharge_decision_config;
pub mod three_ds_decision_rule;
#[cfg(feature = "olap")]
pub mod user;
#[cfg(feature = "olap")]
pub mod user_role;
pub mod utils;
#[cfg(feature = "olap")]
pub mod verification;
#[cfg(feature = "olap")]
pub mod verify_connector;
pub mod webhooks;

pub mod profile_acquirer;
pub mod unified_authentication_service;
pub mod unified_connector_service;

#[cfg(feature = "v2")]
pub mod proxy;
pub mod relay;
#[cfg(feature = "v2")]
pub mod revenue_recovery;

pub mod chat;
pub mod tokenization;


pub struct GpaymentsPostAuthenticationResponse {
    pub authentication_value: Option<Secret<String>>,
    pub trans_status: AuthStatus,
    pub eci: Option<String>,
}


pub struct StoreCardRespPayload {
    pub card_reference: String,
    pub duplication_check: Option<DataDuplicationCheck>,
}