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

pub mod transformers;

use std::sync::LazyLock;

use api_models::webhooks::IncomingWebhookEvent;
use base64::Engine;
use common_enums::enums;
use common_utils::{
    consts::BASE64_ENGINE,
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    payment_method_data::{PayLaterData, PaymentMethodData},
    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,
        PaymentsSessionRouterData, PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    consts::NO_ERROR_MESSAGE,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks::{IncomingWebhook, IncomingWebhookRequestDetails},
};
use masking::{Mask, PeekInterface};
use router_env::logger;
use transformers as klarna;

use crate::{
    constants::headers,
    types::ResponseRouterData,
    utils::{
        convert_amount, get_http_header, get_unimplemented_payment_method_error_message,
        missing_field_err, RefundsRequestData,
    },
};

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

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

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

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

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

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = klarna::KlarnaAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        let encoded_api_key =
            BASE64_ENGINE.encode(format!("{}:{}", auth.username.peek(), auth.password.peek()));
        Ok(vec![(
            headers::AUTHORIZATION.to_string(),
            format!("Basic {encoded_api_key}").into_masked(),
        )])
    }

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

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

        // KlarnaErrorResponse will either have error_messages or error_message field Ref: https://docs.klarna.com/api/errors/
        let reason = response
            .error_messages
            .map(|messages| messages.join(" & "))
            .or(response.error_message.clone());
        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error_code,
            message: response
                .error_message
                .unwrap_or(NO_ERROR_MESSAGE.to_string()),
            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 Klarna {}

impl api::Payment for Klarna {}

impl api::PaymentAuthorize for Klarna {}
impl api::PaymentSync for Klarna {}
impl api::PaymentVoid for Klarna {}
impl api::PaymentCapture for Klarna {}
impl api::PaymentSession for Klarna {}
impl api::ConnectorAccessToken for Klarna {}
impl api::PaymentToken for Klarna {}

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

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Klarna {
    // Not Implemented (R)
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Klarna
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(),
            types::PaymentsAuthorizeType::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 build_region_specific_endpoint(
    base_url: &str,
    connector_metadata: &Option<common_utils::pii::SecretSerdeValue>,
) -> CustomResult<String, errors::ConnectorError> {
    let klarna_metadata_object =
        transformers::KlarnaConnectorMetadataObject::try_from(connector_metadata)?;
    let klarna_region = klarna_metadata_object
        .klarna_region
        .ok_or(errors::ConnectorError::InvalidConnectorConfig {
            config: "merchant_connector_account.metadata.klarna_region",
        })
        .map(String::from)?;

    Ok(base_url.replace("{{klarna_region}}", &klarna_region))
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Klarna {
    fn get_headers(
        &self,
        req: &PaymentsSessionRouterData,
        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: &PaymentsSessionRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint =
            build_region_specific_endpoint(self.base_url(connectors), &req.connector_meta_data)?;

        Ok(format!("{endpoint}payments/v1/sessions"))
    }

    fn get_request_body(
        &self,
        req: &PaymentsSessionRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;
        let connector_router_data = klarna::KlarnaRouterData::from((amount, req));

        let connector_req = klarna::KlarnaSessionRequest::try_from(&connector_router_data)?;
        // encode only for for urlencoded things.
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsSessionRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSessionRouterData, errors::ConnectorError> {
        let response: klarna::KlarnaSessionResponse = res
            .response
            .parse_struct("KlarnaSessionResponse")
            .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 api::MandateSetup for Klarna {}

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

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

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

    fn get_url(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let order_id = req.request.connector_transaction_id.clone();
        let endpoint =
            build_region_specific_endpoint(self.base_url(connectors), &req.connector_meta_data)?;

        Ok(format!(
            "{endpoint}ordermanagement/v1/orders/{order_id}/captures"
        ))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount_to_capture,
            req.request.currency,
        )?;
        let connector_router_data = klarna::KlarnaRouterData::from((amount, req));
        let connector_req = klarna::KlarnaCaptureRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let request = 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();
        Ok(Some(request))
    }

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
        match res.headers {
            Some(headers) => {
                let capture_id = get_http_header("Capture-Id", &headers)
                    .attach_printable("Missing capture id in headers")
                    .change_context(errors::ConnectorError::ResponseHandlingFailed)?;
                let response = klarna::KlarnaCaptureResponse {
                    capture_id: Some(capture_id.to_owned()),
                };

                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)
            }
            None => Err(errors::ConnectorError::ResponseDeserializationFailed)
                .attach_printable("Expected headers, but received no headers in response")?,
        }
    }

    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 Klarna {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let order_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;
        let endpoint =
            build_region_specific_endpoint(self.base_url(connectors), &req.connector_meta_data)?;

        let payment_experience = req.request.payment_experience;

        match payment_experience {
            Some(common_enums::PaymentExperience::InvokeSdkClient) => {
                Ok(format!("{endpoint}ordermanagement/v1/orders/{order_id}"))
            }
            Some(common_enums::PaymentExperience::RedirectToUrl) => {
                Ok(format!("{endpoint}checkout/v3/orders/{order_id}"))
            }
            None => Err(error_stack::report!(errors::ConnectorError::NotSupported {
                message: "payment_experience not supported".to_string(),
                connector: "klarna",
            })),
            _ => Err(error_stack::report!(errors::ConnectorError::NotSupported {
                message: "payment_experience not supported".to_string(),
                connector: "klarna",
            })),
        }
    }

    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: klarna::KlarnaPsyncResponse = res
            .response
            .parse_struct("klarna KlarnaPsyncResponse")
            .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 Klarna {
    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> {
        let payment_method_data = &req.request.payment_method_data;
        let payment_experience = req
            .request
            .payment_experience
            .as_ref()
            .ok_or_else(missing_field_err("payment_experience"))?;
        let payment_method_type = req
            .request
            .payment_method_type
            .as_ref()
            .ok_or_else(missing_field_err("payment_method_type"))?;
        let endpoint =
            build_region_specific_endpoint(self.base_url(connectors), &req.connector_meta_data)?;

        match payment_method_data {
            PaymentMethodData::PayLater(PayLaterData::KlarnaSdk { token }) => {
                match (payment_experience, payment_method_type) {
                    (
                        common_enums::PaymentExperience::InvokeSdkClient,
                        common_enums::PaymentMethodType::Klarna,
                    ) => Ok(format!(
                        "{endpoint}payments/v1/authorizations/{token}/order",
                    )),
                    #[cfg(feature = "v1")]
                    (
                        common_enums::PaymentExperience::DisplayQrCode
                        | common_enums::PaymentExperience::DisplayWaitScreen
                        | common_enums::PaymentExperience::InvokePaymentApp
                        | common_enums::PaymentExperience::InvokeSdkClient
                        | common_enums::PaymentExperience::LinkWallet
                        | common_enums::PaymentExperience::OneClick
                        | common_enums::PaymentExperience::RedirectToUrl
                        | common_enums::PaymentExperience::CollectOtp,
                        common_enums::PaymentMethodType::Ach
                        | common_enums::PaymentMethodType::Bluecode
                        | common_enums::PaymentMethodType::Affirm
                        | common_enums::PaymentMethodType::AfterpayClearpay
                        | common_enums::PaymentMethodType::Alfamart
                        | common_enums::PaymentMethodType::AliPay
                        | common_enums::PaymentMethodType::AliPayHk
                        | common_enums::PaymentMethodType::Alma
                        | common_enums::PaymentMethodType::AmazonPay
                        | common_enums::PaymentMethodType::BhnCardNetwork
                        | common_enums::PaymentMethodType::Paysera
                        | common_enums::PaymentMethodType::Skrill
                        | common_enums::PaymentMethodType::ApplePay
                        | common_enums::PaymentMethodType::Atome
                        | common_enums::PaymentMethodType::Bacs
                        | common_enums::PaymentMethodType::BancontactCard
                        | common_enums::PaymentMethodType::Becs
                        | common_enums::PaymentMethodType::Benefit
                        | common_enums::PaymentMethodType::Bizum
                        | common_enums::PaymentMethodType::Blik
                        | common_enums::PaymentMethodType::Boleto
                        | common_enums::PaymentMethodType::BcaBankTransfer
                        | common_enums::PaymentMethodType::BniVa
                        | common_enums::PaymentMethodType::BriVa
                        | common_enums::PaymentMethodType::CardRedirect
                        | common_enums::PaymentMethodType::CimbVa
                        | common_enums::PaymentMethodType::ClassicReward
                        | common_enums::PaymentMethodType::Credit
                        | common_enums::PaymentMethodType::CryptoCurrency
                        | common_enums::PaymentMethodType::Cashapp
                        | common_enums::PaymentMethodType::Dana
                        | common_enums::PaymentMethodType::DanamonVa
                        | common_enums::PaymentMethodType::Debit
                        | common_enums::PaymentMethodType::DirectCarrierBilling
                        | common_enums::PaymentMethodType::Efecty
                        | common_enums::PaymentMethodType::Eft
                        | common_enums::PaymentMethodType::Eps
                        | common_enums::PaymentMethodType::Evoucher
                        | common_enums::PaymentMethodType::Giropay
                        | common_enums::PaymentMethodType::Givex
                        | common_enums::PaymentMethodType::GooglePay
                        | common_enums::PaymentMethodType::GoPay
                        | common_enums::PaymentMethodType::Gcash
                        | common_enums::PaymentMethodType::Ideal
                        | common_enums::PaymentMethodType::Interac
                        | common_enums::PaymentMethodType::Indomaret
                        | common_enums::PaymentMethodType::Klarna
                        | common_enums::PaymentMethodType::KakaoPay
                        | common_enums::PaymentMethodType::MandiriVa
                        | common_enums::PaymentMethodType::Knet
                        | common_enums::PaymentMethodType::MbWay
                        | common_enums::PaymentMethodType::MobilePay
                        | common_enums::PaymentMethodType::Momo
                        | common_enums::PaymentMethodType::MomoAtm
                        | common_enums::PaymentMethodType::Multibanco
                        | common_enums::PaymentMethodType::LocalBankRedirect
                        | common_enums::PaymentMethodType::OnlineBankingThailand
                        | common_enums::PaymentMethodType::OnlineBankingCzechRepublic
                        | common_enums::PaymentMethodType::OnlineBankingFinland
                        | common_enums::PaymentMethodType::OnlineBankingFpx
                        | common_enums::PaymentMethodType::OnlineBankingPoland
                        | common_enums::PaymentMethodType::OnlineBankingSlovakia
                        | common_enums::PaymentMethodType::Oxxo
                        | common_enums::PaymentMethodType::PagoEfectivo
                        | common_enums::PaymentMethodType::PermataBankTransfer
                        | common_enums::PaymentMethodType::OpenBankingUk
                        | common_enums::PaymentMethodType::PayBright
                        | common_enums::PaymentMethodType::Paypal
                        | common_enums::PaymentMethodType::Paze
                        | common_enums::PaymentMethodType::Pix
                        | common_enums::PaymentMethodType::PaySafeCard
                        | common_enums::PaymentMethodType::Przelewy24
                        | common_enums::PaymentMethodType::Pse
                        | common_enums::PaymentMethodType::RedCompra
                        | common_enums::PaymentMethodType::RedPagos
                        | common_enums::PaymentMethodType::SamsungPay
                        | common_enums::PaymentMethodType::Sepa
                        | common_enums::PaymentMethodType::SepaBankTransfer
                        | common_enums::PaymentMethodType::Sofort
                        | common_enums::PaymentMethodType::Swish
                        | common_enums::PaymentMethodType::TouchNGo
                        | common_enums::PaymentMethodType::Trustly
                        | common_enums::PaymentMethodType::Twint
                        | common_enums::PaymentMethodType::UpiCollect
                        | common_enums::PaymentMethodType::UpiIntent
                        | common_enums::PaymentMethodType::Venmo
                        | common_enums::PaymentMethodType::Vipps
                        | common_enums::PaymentMethodType::Walley
                        | common_enums::PaymentMethodType::WeChatPay
                        | common_enums::PaymentMethodType::SevenEleven
                        | common_enums::PaymentMethodType::Lawson
                        | common_enums::PaymentMethodType::LocalBankTransfer
                        | common_enums::PaymentMethodType::InstantBankTransfer
                        | common_enums::PaymentMethodType::InstantBankTransferFinland
                        | common_enums::PaymentMethodType::InstantBankTransferPoland
                        | common_enums::PaymentMethodType::MiniStop
                        | common_enums::PaymentMethodType::FamilyMart
                        | common_enums::PaymentMethodType::Seicomart
                        | common_enums::PaymentMethodType::PayEasy
                        | common_enums::PaymentMethodType::Mifinity
                        | common_enums::PaymentMethodType::Fps
                        | common_enums::PaymentMethodType::DuitNow
                        | common_enums::PaymentMethodType::PromptPay
                        | common_enums::PaymentMethodType::VietQr
                        | common_enums::PaymentMethodType::Flexiti
                        | common_enums::PaymentMethodType::OpenBankingPIS
                        | common_enums::PaymentMethodType::RevolutPay
                        | common_enums::PaymentMethodType::IndonesianBankTransfer
                        | common_enums::PaymentMethodType::Breadpay,
                    ) => Err(error_stack::report!(errors::ConnectorError::NotSupported {
                        message: payment_method_type.to_string(),
                        connector: "klarna",
                    })),
                    #[cfg(feature = "v2")]
                    (
                        common_enums::PaymentExperience::DisplayQrCode
                        | common_enums::PaymentExperience::DisplayWaitScreen
                        | common_enums::PaymentExperience::InvokePaymentApp
                        | common_enums::PaymentExperience::InvokeSdkClient
                        | common_enums::PaymentExperience::LinkWallet
                        | common_enums::PaymentExperience::OneClick
                        | common_enums::PaymentExperience::RedirectToUrl
                        | common_enums::PaymentExperience::CollectOtp,
                        common_enums::PaymentMethodType::Ach
                        | common_enums::PaymentMethodType::Bluecode
                        | common_enums::PaymentMethodType::Affirm
                        | common_enums::PaymentMethodType::AfterpayClearpay
                        | common_enums::PaymentMethodType::Alfamart
                        | common_enums::PaymentMethodType::AliPay
                        | common_enums::PaymentMethodType::AliPayHk
                        | common_enums::PaymentMethodType::Alma
                        | common_enums::PaymentMethodType::AmazonPay
                        | common_enums::PaymentMethodType::Paysera
                        | common_enums::PaymentMethodType::Skrill
                        | common_enums::PaymentMethodType::ApplePay
                        | common_enums::PaymentMethodType::Atome
                        | common_enums::PaymentMethodType::Bacs
                        | common_enums::PaymentMethodType::BancontactCard
                        | common_enums::PaymentMethodType::Becs
                        | common_enums::PaymentMethodType::Benefit
                        | common_enums::PaymentMethodType::Bizum
                        | common_enums::PaymentMethodType::Blik
                        | common_enums::PaymentMethodType::Boleto
                        | common_enums::PaymentMethodType::BcaBankTransfer
                        | common_enums::PaymentMethodType::BhnCardNetwork
                        | common_enums::PaymentMethodType::BniVa
                        | common_enums::PaymentMethodType::BriVa
                        | common_enums::PaymentMethodType::CardRedirect
                        | common_enums::PaymentMethodType::CimbVa
                        | common_enums::PaymentMethodType::ClassicReward
                        | common_enums::PaymentMethodType::Credit
                        | common_enums::PaymentMethodType::Card
                        | common_enums::PaymentMethodType::CryptoCurrency
                        | common_enums::PaymentMethodType::Cashapp
                        | common_enums::PaymentMethodType::Dana
                        | common_enums::PaymentMethodType::DanamonVa
                        | common_enums::PaymentMethodType::Debit
                        | common_enums::PaymentMethodType::DirectCarrierBilling
                        | common_enums::PaymentMethodType::Efecty
                        | common_enums::PaymentMethodType::Eft
                        | common_enums::PaymentMethodType::Eps
                        | common_enums::PaymentMethodType::Evoucher
                        | common_enums::PaymentMethodType::Giropay
                        | common_enums::PaymentMethodType::Givex
                        | common_enums::PaymentMethodType::GooglePay
                        | common_enums::PaymentMethodType::GoPay
                        | common_enums::PaymentMethodType::Gcash
                        | common_enums::PaymentMethodType::Ideal
                        | common_enums::PaymentMethodType::Interac
                        | common_enums::PaymentMethodType::Indomaret
                        | common_enums::PaymentMethodType::Klarna
                        | common_enums::PaymentMethodType::KakaoPay
                        | common_enums::PaymentMethodType::MandiriVa
                        | common_enums::PaymentMethodType::Knet
                        | common_enums::PaymentMethodType::MbWay
                        | common_enums::PaymentMethodType::MobilePay
                        | common_enums::PaymentMethodType::Momo
                        | common_enums::PaymentMethodType::MomoAtm
                        | common_enums::PaymentMethodType::Multibanco
                        | common_enums::PaymentMethodType::LocalBankRedirect
                        | common_enums::PaymentMethodType::OnlineBankingThailand
                        | common_enums::PaymentMethodType::OnlineBankingCzechRepublic
                        | common_enums::PaymentMethodType::OnlineBankingFinland
                        | common_enums::PaymentMethodType::OnlineBankingFpx
                        | common_enums::PaymentMethodType::OnlineBankingPoland
                        | common_enums::PaymentMethodType::OnlineBankingSlovakia
                        | common_enums::PaymentMethodType::Oxxo
                        | common_enums::PaymentMethodType::PagoEfectivo
                        | common_enums::PaymentMethodType::PermataBankTransfer
                        | common_enums::PaymentMethodType::OpenBankingUk
                        | common_enums::PaymentMethodType::PayBright
                        | common_enums::PaymentMethodType::Paypal
                        | common_enums::PaymentMethodType::Paze
                        | common_enums::PaymentMethodType::Pix
                        | common_enums::PaymentMethodType::PaySafeCard
                        | common_enums::PaymentMethodType::Przelewy24
                        | common_enums::PaymentMethodType::Pse
                        | common_enums::PaymentMethodType::RedCompra
                        | common_enums::PaymentMethodType::RedPagos
                        | common_enums::PaymentMethodType::SamsungPay
                        | common_enums::PaymentMethodType::Sepa
                        | common_enums::PaymentMethodType::SepaBankTransfer
                        | common_enums::PaymentMethodType::Sofort
                        | common_enums::PaymentMethodType::Swish
                        | common_enums::PaymentMethodType::TouchNGo
                        | common_enums::PaymentMethodType::Trustly
                        | common_enums::PaymentMethodType::Twint
                        | common_enums::PaymentMethodType::UpiCollect
                        | common_enums::PaymentMethodType::UpiIntent
                        | common_enums::PaymentMethodType::Venmo
                        | common_enums::PaymentMethodType::Vipps
                        | common_enums::PaymentMethodType::Walley
                        | common_enums::PaymentMethodType::WeChatPay
                        | common_enums::PaymentMethodType::SevenEleven
                        | common_enums::PaymentMethodType::Lawson
                        | common_enums::PaymentMethodType::LocalBankTransfer
                        | common_enums::PaymentMethodType::InstantBankTransfer
                        | common_enums::PaymentMethodType::InstantBankTransferFinland
                        | common_enums::PaymentMethodType::InstantBankTransferPoland
                        | common_enums::PaymentMethodType::MiniStop
                        | common_enums::PaymentMethodType::FamilyMart
                        | common_enums::PaymentMethodType::Seicomart
                        | common_enums::PaymentMethodType::PayEasy
                        | common_enums::PaymentMethodType::Mifinity
                        | common_enums::PaymentMethodType::Fps
                        | common_enums::PaymentMethodType::DuitNow
                        | common_enums::PaymentMethodType::PromptPay
                        | common_enums::PaymentMethodType::VietQr
                        | common_enums::PaymentMethodType::Flexiti
                        | common_enums::PaymentMethodType::OpenBankingPIS
                        | common_enums::PaymentMethodType::Skrill
                        | common_enums::PaymentMethodType::IndonesianBankTransfer
                        | common_enums::PaymentMethodType::RevolutPay
                        | common_enums::PaymentMethodType::Breadpay,
                    ) => Err(error_stack::report!(errors::ConnectorError::NotSupported {
                        message: payment_method_type.to_string(),
                        connector: "klarna",
                    })),
                }
            }
            PaymentMethodData::PayLater(PayLaterData::KlarnaRedirect {}) => {
                match (payment_experience, payment_method_type) {
                    (
                        common_enums::PaymentExperience::RedirectToUrl,
                        common_enums::PaymentMethodType::Klarna,
                    ) => Ok(format!("{endpoint}checkout/v3/orders")),
                    #[cfg(feature = "v1")]
                    (
                        common_enums::PaymentExperience::DisplayQrCode
                        | common_enums::PaymentExperience::DisplayWaitScreen
                        | common_enums::PaymentExperience::InvokePaymentApp
                        | common_enums::PaymentExperience::InvokeSdkClient
                        | common_enums::PaymentExperience::LinkWallet
                        | common_enums::PaymentExperience::OneClick
                        | common_enums::PaymentExperience::RedirectToUrl
                        | common_enums::PaymentExperience::CollectOtp,
                        common_enums::PaymentMethodType::Ach
                        | common_enums::PaymentMethodType::Bluecode
                        | common_enums::PaymentMethodType::Affirm
                        | common_enums::PaymentMethodType::AfterpayClearpay
                        | common_enums::PaymentMethodType::Alfamart
                        | common_enums::PaymentMethodType::AliPay
                        | common_enums::PaymentMethodType::AliPayHk
                        | common_enums::PaymentMethodType::Alma
                        | common_enums::PaymentMethodType::AmazonPay
                        | common_enums::PaymentMethodType::Paysera
                        | common_enums::PaymentMethodType::Skrill
                        | common_enums::PaymentMethodType::ApplePay
                        | common_enums::PaymentMethodType::Atome
                        | common_enums::PaymentMethodType::Bacs
                        | common_enums::PaymentMethodType::BancontactCard
                        | common_enums::PaymentMethodType::Becs
                        | common_enums::PaymentMethodType::Benefit
                        | common_enums::PaymentMethodType::Bizum
                        | common_enums::PaymentMethodType::Blik
                        | common_enums::PaymentMethodType::Boleto
                        | common_enums::PaymentMethodType::BhnCardNetwork
                        | common_enums::PaymentMethodType::BcaBankTransfer
                        | common_enums::PaymentMethodType::BniVa
                        | common_enums::PaymentMethodType::BriVa
                        | common_enums::PaymentMethodType::CardRedirect
                        | common_enums::PaymentMethodType::CimbVa
                        | common_enums::PaymentMethodType::ClassicReward
                        | common_enums::PaymentMethodType::Credit
                        | common_enums::PaymentMethodType::CryptoCurrency
                        | common_enums::PaymentMethodType::Cashapp
                        | common_enums::PaymentMethodType::Dana
                        | common_enums::PaymentMethodType::DanamonVa
                        | common_enums::PaymentMethodType::Debit
                        | common_enums::PaymentMethodType::DirectCarrierBilling
                        | common_enums::PaymentMethodType::Efecty
                        | common_enums::PaymentMethodType::Eft
                        | common_enums::PaymentMethodType::Eps
                        | common_enums::PaymentMethodType::Evoucher
                        | common_enums::PaymentMethodType::Giropay
                        | common_enums::PaymentMethodType::Givex
                        | common_enums::PaymentMethodType::GooglePay
                        | common_enums::PaymentMethodType::GoPay
                        | common_enums::PaymentMethodType::Gcash
                        | common_enums::PaymentMethodType::Ideal
                        | common_enums::PaymentMethodType::Interac
                        | common_enums::PaymentMethodType::Indomaret


// Implementation: impl super::refunds::distribution::RefundDistributionAnalytics for for ClickhouseClient
// File: crates/analytics/src/clickhouse.rs
// Module: analytics
// Methods: 0 total (0 public)

impl super::refunds::distribution::RefundDistributionAnalytics for for ClickhouseClient


// Struct: SantanderAuthType
// File: crates/hyperswitch_connectors/src/connectors/santander/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SantanderAuthType


// Struct: ConnectorTomlConfig
// File: crates/connector_configs/src/connector.rs
// Module: connector_configs
// Implementations: 0

pub struct ConnectorTomlConfig


// Function: get_session_token_for_click_to_pay
// File: crates/router/src/core/payments.rs
// Module: router

pub fn get_session_token_for_click_to_pay(
    state: &SessionState,
    merchant_id: &id_type::MerchantId,
    merchant_context: &domain::MerchantContext,
    authentication_product_ids: common_types::payments::AuthenticationConnectorAccountMap,
    payment_intent: &payments::PaymentIntent,
    profile_id: &id_type::ProfileId,
) -> RouterResult<api_models::payments::SessionToken>


// Module Structure
// File: crates/router/src/routes/files.rs
// Module: router

// Public submodules:
pub mod transformers;


// Struct: SilverflowRouterData
// File: crates/hyperswitch_connectors/src/connectors/silverflow/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SilverflowRouterData<T>


// Implementation: impl api::Refund for for Paytm
// File: crates/hyperswitch_connectors/src/connectors/paytm.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Refund for for Paytm


// Struct: NoonRefundSyncResponseResult
// File: crates/hyperswitch_connectors/src/connectors/noon/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NoonRefundSyncResponseResult


// Implementation: impl api::MandateSetup for for Globepay
// File: crates/hyperswitch_connectors/src/connectors/globepay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::MandateSetup for for Globepay


// Implementation: impl PartialEq for for SdkEventMetricsBucketIdentifier
// File: crates/api_models/src/analytics/sdk_events.rs
// Module: api_models
// Methods: 1 total (0 public)

impl PartialEq for for SdkEventMetricsBucketIdentifier


// Struct: AffirmErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/affirm/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AffirmErrorResponse


// Struct: CaptureIntegrityObject
// File: crates/hyperswitch_domain_models/src/router_request_types.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct CaptureIntegrityObject


// File: crates/storage_impl/src/payments/payment_attempt.rs
// Module: storage_impl

use common_utils::errors::CustomResult;
#[cfg(feature = "v2")]
use common_utils::types::keymanager::KeyManagerState;
#[cfg(feature = "v1")]
use common_utils::{
    fallback_reverse_lookup_not_found,
    types::{ConnectorTransactionId, ConnectorTransactionIdTrait, CreatedBy},
};
#[cfg(feature = "v1")]
use diesel_models::payment_attempt::PaymentAttemptNew as DieselPaymentAttemptNew;
use diesel_models::{
    enums::{
        MandateAmountData as DieselMandateAmountData, MandateDataType as DieselMandateType,
        MandateDetails as DieselMandateDetails, MerchantStorageScheme,
    },
    kv,
    payment_attempt::PaymentAttempt as DieselPaymentAttempt,
    reverse_lookup::{ReverseLookup, ReverseLookupNew},
};
use error_stack::ResultExt;
#[cfg(feature = "v1")]
use hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptNew;
#[cfg(feature = "v2")]
use hyperswitch_domain_models::{
    behaviour::{Conversion, ReverseConversion},
    merchant_key_store::MerchantKeyStore,
};
use hyperswitch_domain_models::{
    mandates::{MandateAmountData, MandateDataType, MandateDetails},
    payments::payment_attempt::{PaymentAttempt, PaymentAttemptInterface, PaymentAttemptUpdate},
};
#[cfg(all(feature = "v1", feature = "olap"))]
use hyperswitch_domain_models::{
    payments::payment_attempt::PaymentListFilters, payments::PaymentIntent,
};
#[cfg(feature = "v2")]
use label::*;
use redis_interface::HsetnxReply;
use router_env::{instrument, tracing};

#[cfg(feature = "v2")]
use crate::kv_router_store::{FilterResourceParams, FindResourceBy, UpdateResourceParams};
use crate::{
    diesel_error_to_data_error, errors,
    errors::RedisErrorExt,
    kv_router_store::KVRouterStore,
    lookup::ReverseLookupInterface,
    redis::kv_store::{decide_storage_scheme, kv_wrapper, KvOperation, Op, PartitionKey},
    utils::{pg_connection_read, pg_connection_write, try_redis_get_else_try_database_get},
    DataModelExt, DatabaseStore, RouterStore,
};

#[async_trait::async_trait]
impl<T: DatabaseStore> PaymentAttemptInterface for RouterStore<T> {
    type Error = errors::StorageError;
    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn insert_payment_attempt(
        &self,
        payment_attempt: PaymentAttemptNew,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_write(self).await?;
        payment_attempt
            .to_storage_model()
            .insert(&conn)
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
            .map(PaymentAttempt::from_storage_model)
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn insert_payment_attempt(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        payment_attempt: PaymentAttempt,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_write(self).await?;
        payment_attempt
            .construct_new()
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .insert(&conn)
            .await
            .map_err(|error| {
                let new_error = diesel_error_to_data_error(*error.current_context());
                error.change_context(new_error)
            })?
            .convert(
                key_manager_state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn update_payment_attempt_with_attempt_id(
        &self,
        this: PaymentAttempt,
        payment_attempt: PaymentAttemptUpdate,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_write(self).await?;
        this.to_storage_model()
            .update_with_attempt_id(&conn, payment_attempt.to_storage_model())
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
            .map(PaymentAttempt::from_storage_model)
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn update_payment_attempt(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        this: PaymentAttempt,
        payment_attempt: PaymentAttemptUpdate,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_write(self).await?;

        Conversion::convert(this)
            .await
            .change_context(errors::StorageError::EncryptionError)?
            .update_with_attempt_id(
                &conn,
                diesel_models::PaymentAttemptUpdateInternal::from(payment_attempt),
            )
            .await
            .map_err(|error| {
                let new_error = diesel_error_to_data_error(*error.current_context());
                error.change_context(new_error)
            })?
            .convert(
                key_manager_state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
        &self,
        connector_transaction_id: &ConnectorTransactionId,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPaymentAttempt::find_by_connector_transaction_id_payment_id_merchant_id(
            &conn,
            connector_transaction_id,
            payment_id,
            merchant_id,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
        .map(PaymentAttempt::from_storage_model)
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id(
        &self,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPaymentAttempt::find_last_successful_attempt_by_payment_id_merchant_id(
            &conn,
            payment_id,
            merchant_id,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
        .map(PaymentAttempt::from_storage_model)
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id(
        &self,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPaymentAttempt::find_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id(
            &conn,
            payment_id,
            merchant_id,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
        .map(PaymentAttempt::from_storage_model)
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        payment_id: &common_utils::id_type::GlobalPaymentId,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPaymentAttempt::find_last_successful_or_partially_captured_attempt_by_payment_id(
            &conn, payment_id,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })?
        .convert(
            key_manager_state,
            merchant_key_store.key.get_inner(),
            merchant_key_store.merchant_id.clone().into(),
        )
        .await
        .change_context(errors::StorageError::DecryptionError)
    }

    #[instrument(skip_all)]
    #[cfg(feature = "v1")]
    async fn find_payment_attempt_by_merchant_id_connector_txn_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_txn_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPaymentAttempt::find_by_merchant_id_connector_txn_id(
            &conn,
            merchant_id,
            connector_txn_id,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
        .map(PaymentAttempt::from_storage_model)
    }

    #[instrument(skip_all)]
    #[cfg(feature = "v2")]
    async fn find_payment_attempt_by_profile_id_connector_transaction_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        profile_id: &common_utils::id_type::ProfileId,
        connector_txn_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPaymentAttempt::find_by_profile_id_connector_transaction_id(
            &conn,
            profile_id,
            connector_txn_id,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })?
        .convert(
            key_manager_state,
            merchant_key_store.key.get_inner(),
            merchant_key_store.merchant_id.clone().into(),
        )
        .await
        .change_context(errors::StorageError::DecryptionError)
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id(
        &self,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        attempt_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;

        DieselPaymentAttempt::find_by_payment_id_merchant_id_attempt_id(
            &conn,
            payment_id,
            merchant_id,
            attempt_id,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
        .map(PaymentAttempt::from_storage_model)
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    #[instrument(skip_all)]
    async fn get_filters_for_payments(
        &self,
        pi: &[PaymentIntent],
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentListFilters, errors::StorageError> {
        use hyperswitch_domain_models::behaviour::Conversion;

        let conn = pg_connection_read(self).await?;
        let intents = futures::future::try_join_all(pi.iter().cloned().map(|pi| async {
            Conversion::convert(pi)
                .await
                .change_context(errors::StorageError::EncryptionError)
        }))
        .await?;

        DieselPaymentAttempt::get_filters_for_payments(&conn, intents.as_slice(), merchant_id)
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
            .map(
                |(
                    connector,
                    currency,
                    status,
                    payment_method,
                    payment_method_type,
                    authentication_type,
                )| PaymentListFilters {
                    connector,
                    currency,
                    status,
                    payment_method,
                    payment_method_type,
                    authentication_type,
                },
            )
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn find_payment_attempt_by_preprocessing_id_merchant_id(
        &self,
        preprocessing_id: &str,
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;

        DieselPaymentAttempt::find_by_merchant_id_preprocessing_id(
            &conn,
            merchant_id,
            preprocessing_id,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
        .map(PaymentAttempt::from_storage_model)
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn find_attempts_by_merchant_id_payment_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payment_id: &common_utils::id_type::PaymentId,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<Vec<PaymentAttempt>, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPaymentAttempt::find_by_merchant_id_payment_id(&conn, merchant_id, payment_id)
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
            .map(|a| {
                a.into_iter()
                    .map(PaymentAttempt::from_storage_model)
                    .collect()
            })
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn find_payment_attempt_by_attempt_id_merchant_id(
        &self,
        attempt_id: &str,
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;

        DieselPaymentAttempt::find_by_merchant_id_attempt_id(&conn, merchant_id, attempt_id)
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
            .map(PaymentAttempt::from_storage_model)
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn find_payment_attempt_by_id(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        attempt_id: &common_utils::id_type::GlobalAttemptId,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;

        DieselPaymentAttempt::find_by_id(&conn, attempt_id)
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })?
            .convert(
                key_manager_state,
                merchant_key_store.key.get_inner(),
                merchant_key_store.merchant_id.clone().into(),
            )
            .await
            .change_context(errors::StorageError::DecryptionError)
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn find_payment_attempts_by_payment_intent_id(
        &self,
        key_manager_state: &KeyManagerState,
        payment_id: &common_utils::id_type::GlobalPaymentId,
        merchant_key_store: &MerchantKeyStore,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<Vec<PaymentAttempt>, errors::StorageError> {
        use common_utils::ext_traits::AsyncExt;

        let conn = pg_connection_read(self).await?;
        DieselPaymentAttempt::find_by_payment_id(&conn, payment_id)
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
            .async_and_then(|payment_attempts| async {
                let mut domain_payment_attempts = Vec::with_capacity(payment_attempts.len());
                for attempt in payment_attempts.into_iter() {
                    domain_payment_attempts.push(
                        attempt
                            .convert(
                                key_manager_state,
                                merchant_key_store.key.get_inner(),
                                merchant_key_store.merchant_id.clone().into(),
                            )
                            .await
                            .change_context(errors::StorageError::DecryptionError)?,
                    );
                }
                Ok(domain_payment_attempts)
            })
            .await
    }

    #[cfg(all(feature = "v1", feature = "olap"))]
    #[instrument(skip_all)]
    async fn get_total_count_of_filtered_payment_attempts(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        active_attempt_ids: &[String],
        connector: Option<Vec<api_models::enums::Connector>>,
        payment_method: Option<Vec<common_enums::PaymentMethod>>,
        payment_method_type: Option<Vec<common_enums::PaymentMethodType>>,
        authentication_type: Option<Vec<common_enums::AuthenticationType>>,
        merchant_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
        card_network: Option<Vec<common_enums::CardNetwork>>,
        card_discovery: Option<Vec<common_enums::CardDiscovery>>,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<i64, errors::StorageError> {
        let conn = self
            .db_store
            .get_replica_pool()
            .get()
            .await
            .change_context(errors::StorageError::DatabaseConnectionError)?;
        let connector_strings = connector.as_ref().map(|connector| {
            connector
                .iter()
                .map(|c| c.to_string())
                .collect::<Vec<String>>()
        });
        DieselPaymentAttempt::get_total_count_of_attempts(
            &conn,
            merchant_id,
            active_attempt_ids,
            connector_strings,
            payment_method,
            payment_method_type,
            authentication_type,
            merchant_connector_id,
            card_network,
            card_discovery,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
    }
    #[cfg(all(feature = "v2", feature = "olap"))]
    #[instrument(skip_all)]
    async fn get_total_count_of_filtered_payment_attempts(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        active_attempt_ids: &[String],
        connector: Option<Vec<api_models::enums::Connector>>,
        payment_method_type: Option<Vec<common_enums::PaymentMethod>>,
        payment_method_subtype: Option<Vec<common_enums::PaymentMethodType>>,
        authentication_type: Option<Vec<common_enums::AuthenticationType>>,
        merchant_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
        card_network: Option<Vec<common_enums::CardNetwork>>,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<i64, errors::StorageError> {
        let conn = self
            .db_store
            .get_replica_pool()
            .get()
            .await
            .change_context(errors::StorageError::DatabaseConnectionError)?;

        DieselPaymentAttempt::get_total_count_of_attempts(
            &conn,
            merchant_id,
            active_attempt_ids,
            connector
                .as_ref()
                .map(|vals| vals.iter().map(|v| v.to_string()).collect()),
            payment_method_type,
            payment_method_subtype,
            authentication_type,
            merchant_connector_id,
            card_network,
        )
        .await
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
    }
}

#[async_trait::async_trait]
impl<T: DatabaseStore> PaymentAttemptInterface for KVRouterStore<T> {
    type Error = errors::StorageError;
    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn insert_payment_attempt(
        &self,
        payment_attempt: PaymentAttemptNew,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, errors::StorageError> {
        let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselPaymentAttempt>(
            self,
            storage_scheme,
            Op::Insert,
        ))
        .await;
        match storage_scheme {
            MerchantStorageScheme::PostgresOnly => {
                self.router_store
                    .insert_payment_attempt(payment_attempt, storage_scheme)
                    .await
            }
            MerchantStorageScheme::RedisKv => {
                let merchant_id = payment_attempt.merchant_id.clone();
                let payment_id = payment_attempt.payment_id.clone();
                let key = PartitionKey::MerchantIdPaymentId {
                    merchant_id: &merchant_id,
                    payment_id: &payment_id,
                };
                let key_str = key.to_string();
                let created_attempt = PaymentAttempt {
                    payment_id: payment_attempt.payment_id.clone(),
                    merchant_id: payment_attempt.merchant_id.clone(),
                    attempt_id: payment_attempt.attempt_id.clone(),
                    status: payment_attempt.status,
                    net_amount: payment_attempt.net_amount.clone(),
                    currency: payment_attempt.currency,
                    save_to_locker: payment_attempt.save_to_locker,
                    connector: payment_attempt.connector.clone(),
                    error_message: payment_attempt.error_message.clone(),
                    offer_amount: payment_attempt.offer_amount,
                    payment_method_id: payment_attempt.payment_method_id.clone(),
                    payment_method: payment_attempt.payment_method,
                    connector_transaction_id: None,
                    capture_method: payment_attempt.capture_method,
                    capture_on: payment_attempt.capture_on,
                    confirm: payment_attempt.confirm,
                    authentication_type: payment_attempt.authentication_type,
                    created_at: payment_attempt
                        .created_at
                        .unwrap_or_else(common_utils::date_time::now),
                    modified_at: payment_attempt
                        .created_at
                        .unwrap_or_else(common_utils::date_time::now),
                    last_synced: payment_attempt.last_synced,
                    amount_to_capture: payment_attempt.amount_to_capture,
                    cancellation_reason: payment_attempt.cancellation_reason.clone(),
                    mandate_id: payment_attempt.mandate_id.clone(),
                    browser_info: payment_attempt.browser_info.clone(),
                    payment_token: payment_attempt.payment_token.clone(),
                    error_code: payment_attempt.error_code.clone(),
                    connector_metadata: payment_attempt.connector_metadata.clone(),
                    payment_experience: payment_attempt.payment_experience,
                    payment_method_type: payment_attempt.payment_method_type,
                    payment_method_data: payment_attempt.payment_method_data.clone(),
                    business_sub_label: payment_attempt.business_sub_label.clone(),
                    straight_through_algorithm: payment_attempt.straight_through_algorithm.clone(),
                    mandate_details: payment_attempt.mandate_details.clone(),
                    preprocessing_step_id: payment_attempt.preprocessing_step_id.clone(),
                    error_reason: payment_attempt.error_reason.clone(),
                    multiple_capture_count: payment_attempt.multiple_capture_count,
                    connector_response_reference_id: None,
                    charge_id: None,
                    amount_capturable: payment_attempt.amount_capturable,
                    updated_by: storage_scheme.to_string(),
                    authentication_data: payment_attempt.authentication_data.clone(),
                    encoded_data: payment_attempt.encoded_data.clone(),
                    merchant_connector_id: payment_attempt.merchant_connector_id.clone(),
                    unified_code: payment_attempt.unified_code.clone(),
                    unified_message: payment_attempt.unified_message.clone(),
                    external_three_ds_authentication_attempted: payment_attempt
                        .external_three_ds_authentication_attempted,
                    authentication_connector: payment_attempt.authentication_connector.clone(),
                    authentication_id: payment_attempt.authentication_id.clone(),
                    mandate_data: payment_attempt.mandate_data.clone(),
                    payment_method_billing_address_id: payment_attempt
                        .payment_method_billing_address_id
                        .clone(),
                    fingerprint_id: payment_attempt.fingerprint_id.clone(),
                    client_source: payment_attempt.client_source.clone(),
                    client_version: payment_attempt.client_version.clone(),
                    customer_acceptance: payment_attempt.customer_acceptance.clone(),
                    organization_id: payment_attempt.organization_id.clone(),
                    profile_id: payment_attempt.profile_id.clone(),
                    connector_mandate_detail: payment_attempt.connector_mandate_detail.clone(),
                    request_extended_authorization: payment_attempt.request_extended_authorization,
                    extended_authorization_applied: payment_attempt.extended_authorization_applied,
                    capture_before: payment_attempt.capture_before,
                    card_discovery: payment_attempt.card_discovery,
                    charges: None,
                    issuer_error_code: None,
                    issuer_error_message: None,
                    processor_merchant_id: payment_attempt.processor_merchant_id.clone(),
                    created_by: payment_attempt.created_by.clone(),
                    setup_future_usage_applied: payment_attempt.setup_future_usage_applied,
                    routing_approach: payment_attempt.routing_approach.clone(),
                    connector_request_reference_id: payment_attempt
                        .connector_request_reference_id
                        .clone(),
                    debit_routing_savings: None,
                    network_transaction_id: payment_attempt.network_transaction_id.clone(),
                    is_overcapture_enabled: None,
                    network_details: payment_attempt.network_details.clone(),
                    is_stored_credential: payment_attempt.is_stored_credential,
                };

                let field = format!("pa_{}", created_attempt.attempt_id);

                let redis_entry = kv::TypedSql {
                    op: kv::DBOperation::Insert {
                        insertable: Box::new(kv::Insertable::PaymentAttempt(Box::new(
                            payment_attempt.to_storage_model(),
                        ))),
                    },
                };

                //Reverse lookup for attempt_id
                let reverse_lookup = ReverseLookupNew {
                    lookup_id: format!(
                        "pa_{}_{}",
                        created_attempt.merchant_id.get_string_repr(),
                        &created_attempt.attempt_id,
                    ),
                    pk_id: key_str.clone(),
                    sk_id: field.clone(),
                    source: "payment_attempt".to_string(),
                    updated_by: storage_scheme.to_string(),
                };
                self.insert_reverse_lookup(reverse_lookup, storage_scheme)
                    .await?;

                match Box::pin(kv_wrapper::<PaymentAttempt, _, _>(
                    self,
                    KvOperation::HSetNx(
                        &field,
                        &created_attempt.clone().to_storage_model(),
                        redis_entry,
                    ),
                    key,
                ))
                .await
                .map_err(|err| err.to_redis_failed_response(&key_str))?
                .try_into_hsetnx()
                {
                    Ok(HsetnxReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue {
                        entity: "payment attempt",
                        key: Some(key_str),
                    }
                    .into()),
                    Ok(HsetnxReply::KeySet) => Ok(created_attempt),
                    Err(error) => Err(error.change_context(errors::StorageError::KVError)),
                }
            }
        }
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn insert_payment_attempt(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        payment_attempt: PaymentAttempt,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, errors::StorageError> {
        let decided_storage_scheme = Box::pin(decide_storage_scheme::<_, DieselPaymentAttempt>(
            self,
            storage_scheme,
            Op::Insert,
        ))
        .await;

        match decided_storage_scheme {
            MerchantStorageScheme::PostgresOnly => {
                self.router_store
                    .insert_payment_attempt(
                        key_manager_state,
                        merchant_key_store,
                        payment_attempt,
                        decided_storage_scheme,
                    )
                    .await
            }
            MerchantStorageScheme::RedisKv => {
                let key = PartitionKey::GlobalPaymentId {
                    id: &payment_attempt.payment_id,
                };
                let key_str = key.to_string();
                let field = format!(
                    "{}_{}",
                    label::CLUSTER_LABEL,
                    payment_attempt.id.get_string_repr()
                );

                let diesel_payment_attempt_new = payment_attempt
                    .clone()
                    .construct_new()
                    .await
                    .change_context(errors::StorageError::EncryptionError)?;

                let diesel_payment_attempt_for_redis: DieselPaymentAttempt =
                    Conversion::convert(payment_attempt.clone())
                        .await
                        .change_context(errors::StorageError::EncryptionError)?;

                let redis_entry = kv::TypedSql {
                    op: kv::DBOperation::Insert {
                        insertable: Box::new(kv::Insertable::PaymentAttempt(Box::new(
                            diesel_payment_attempt_new.clone(),
                        ))),
                    },
                };

                let reverse_lookup_attempt_id = ReverseLookupNew {
                    lookup_id: label::get_global_id_label(&payment_attempt.id),
                    pk_id: key_str.clone(),
                    sk_id: field.clone(),
                    source: "payment_attempt".to_string(),
                    updated_by: decided_storage_scheme.to_string(),
                };
                self.insert_reverse_lookup(reverse_lookup_attempt_id, decided_storage_scheme)
                    .await?;

                if let Some(ref conn_txn_id_val) = payment_attempt.connector_payment_id {
                    let reverse_lookup_conn_txn_id = ReverseLookupNew {
                        lookup_id: label::get_profile_id_connector_transaction_label(
                            payment_attempt.profile_id.get_string_repr(),
                            conn_txn_id_val,
                        ),
                        pk_id: key_str.clone(),
                        sk_id: field.clone(),
                        source: "payment_attempt".to_string(),
                        updated_by: decided_storage_scheme.to_string(),
                    };
                    self.insert_reverse_lookup(reverse_lookup_conn_txn_id, decided_storage_scheme)
                        .await?;
                }

                match Box::pin(kv_wrapper::<DieselPaymentAttempt, _, _>(
                    self,
                    KvOperation::HSetNx(&field, &diesel_payment_attempt_for_redis, redis_entry),
                    key,
                ))
                .await
                .map_err(|err| err.to_redis_failed_response(&key_str))?
                .try_into_hsetnx()
                {
                    Ok(HsetnxReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue {
                        entity: "payment_attempt",
                        key: Some(payment_attempt.id.get_string_repr().to_owned()),
                    }
                    .into()),
                    Ok(HsetnxReply::KeySet) => Ok(payment_attempt),
                    Err(error) => Err(error.change_context(errors::StorageError::KVError)),
                }
            }
        }
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn update_payment_attempt_with_attempt_id(
        &self,
        this: PaymentAttempt,
        payment_attempt: PaymentAttemptUpdate,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, errors::StorageError> {
        let key = PartitionKey::MerchantIdPaymentId {
            merchant_id: &this.merchant_id,
            payment_id: &this.payment_id,
        };
        let field = format!("pa_{}", this.attempt_id);
        let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselPaymentAttempt>(
            self,
            storage_scheme,
            Op::Update(key.clone(), &field, Some(&this.updated_by)),
        ))
        .await;
        match storage_scheme {
            MerchantStorageScheme::PostgresOnly => {
                self.router_store
                    .update_payment_attempt_with_attempt_id(this, payment_attempt, storage_scheme)
                    .await
            }
            MerchantStorageScheme::RedisKv => {
                let key_str = key.to_string();
                let old_connector_transaction_id = &this.get_connector_payment_id();
                let old_preprocessing_id = &this.preprocessing_step_id;
                let updated_attempt = PaymentAttempt::from_storage_model(
                    payment_attempt
                        .clone()
                        .to_storage_model()
                        .apply_changeset(this.clone().to_storage_model()),
                );
                // Check for database presence as well Maybe use a read replica here ?
                let redis_value = serde_json::to_string(&updated_attempt)
                    .change_context(errors::StorageError::KVError)?;

                let redis_entry = kv::TypedSql {
                    op: kv::DBOperation::Update {
                        updatable: Box::new(kv::Updateable::PaymentAttemptUpdate(Box::new(
                            kv::PaymentAttemptUpdateMems {
                                orig: this.clone().to_storage_model(),
                                update_data: payment_attempt.to_storage_model(),
                            },
                        ))),
                    },
                };

                match (
                    old_connector_transaction_id,
                    &updated_attempt.get_connector_payment_id(),
                ) {
                    (None, Some(connector_transaction_id)) => {
                        add_connector_txn_id_to_reverse_lookup(
                            self,
                            key_str.as_str(),
                            &this.merchant_id,
                            updated_attempt.attempt_id.as_str(),
                            connector_transaction_id,
                            storage_scheme,
                        )
                        .await?;
                    }
                    (Some(old_connector_transaction_id), Some(connector_transaction_id)) => {
                        if old_connector_transaction_id.ne(connector_transaction_id) {
                            add_connector_txn_id_to_reverse_lookup(
                                self,
                                key_str.as_str(),
                                &this.merchant_id,
                                updated_attempt.attempt_id.as_str(),
                                connector_transaction_id,
                                storage_scheme,
                            )
                            .await?;
                        }
                    }
                    (_, _) => {}
                }

                match (old_preprocessing_id, &updated_attempt.preprocessing_step_id) {
                    (None, Some(preprocessing_id)) => {
                        add_preprocessing_id_to_reverse_lookup(
                            self,
                            key_str.as_str(),
                            &this.merchant_id,
                            updated_attempt.attempt_id.as_str(),
                            preprocessing_id.as_str(),
                            storage_scheme,
                        )
                        .await?;
                    }
                    (Some(old_preprocessing_id), Some(preprocessing_id)) => {
                        if old_preprocessing_id.ne(preprocessing_id) {
                            add_preprocessing_id_to_reverse_lookup(
                                self,
                                key_str.as_str(),
                                &this.merchant_id,
                                updated_attempt.attempt_id.as_str(),
                                preprocessing_id.as_str(),
                                storage_scheme,
                            )
                            .await?;
                        }
                    }
                    (_, _) => {}
                }

                Box::pin(kv_wrapper::<(), _, _>(
                    self,
                    KvOperation::Hset::<DieselPaymentAttempt>((&field, redis_value), redis_entry),
                    key,
                ))
                .await
                .change_context(errors::StorageError::KVError)?
                .try_into_hset()
                .change_context(errors::StorageError::KVError)?;

                Ok(updated_attempt)
            }
        }
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn update_payment_attempt(
        &self,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &MerchantKeyStore,
        this: PaymentAttempt,
        payment_attempt_update: PaymentAttemptUpdate,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, errors::StorageError> {
        let payment_attempt = Conversion::convert(this.clone())
            .await
            .change_context(errors::StorageError::DecryptionError)?;

        let key = PartitionKey::GlobalPaymentId {
            id: &this.payment_id,
        };

        let field = format!("{}_{}", label::CLUSTER_LABEL, this.id.get_string_repr());
        let conn = pg_connection_write(self).await?;

        let payment_attempt_internal =
            diesel_models::PaymentAttemptUpdateInternal::from(payment_attempt_update);
        let updated_payment_attempt = payment_attempt_internal
            .clone()
            .apply_changeset(payment_attempt.clone());

        let updated_by = updated_payment_attempt.updated_by.to_owned();
        let updated_payment_attempt_with_id = payment_attempt
            .clone()
            .update_with_attempt_id(&conn, payment_attempt_internal.clone());

        Box::pin(self.update_resource(
            key_manager_state,
            merchant_key_store,
            storage_scheme,
            updated_payment_attempt_with_id,
            updated_payment_attempt,
            UpdateResourceParams {
                updateable: kv::Updateable::PaymentAttemptUpdate(Box::new(
                    kv::PaymentAttemptUpdateMems {
                        orig: payment_attempt,
                        update_data: payment_attempt_internal,
                    },
                )),
                operation: Op::Update(key.clone(), &field, Some(updated_by.as_str())),
            },
        ))
        .await
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
        &self,
        connector_transaction_id: &ConnectorTransactionId,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PaymentAttempt, errors::StorageError> {
        let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselPaymentAttempt>(
            self,
            storage_scheme,
            Op::Find,
        ))
        .await;
        match storage_scheme {
            MerchantStorageScheme::PostgresOnly => {
                self.router_store
                    .find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(
                        connector_transaction_id,
                        payment_id,
                        merchant_id,
                        storage_scheme,
                    )
                    .await
            }
            MerchantStorageScheme::RedisKv => {
                // We assume that PaymentAttempt <=> PaymentIntent is a one-to-one relation for now
                let lookup_id = format!(
                    "pa_conn_trans_{}_{}",
                    merchant_id.get_string_repr(),
                    connector_transaction_id.get_id()
                );
                let lookup = fallback_reverse_lookup_not_found!(
                    self.get_lookup_by_lookup_id(&lookup_id, storage_scheme)
                        .await,
                    self.router_store


// File: crates/drainer/src/metrics.rs
// Module: drainer

use router_env::{counter_metric, global_meter, histogram_metric_f64, histogram_metric_u64};

global_meter!(DRAINER_METER, "DRAINER");

counter_metric!(JOBS_PICKED_PER_STREAM, DRAINER_METER);
counter_metric!(CYCLES_COMPLETED_SUCCESSFULLY, DRAINER_METER);
counter_metric!(CYCLES_COMPLETED_UNSUCCESSFULLY, DRAINER_METER);
counter_metric!(ERRORS_WHILE_QUERY_EXECUTION, DRAINER_METER);
counter_metric!(SUCCESSFUL_QUERY_EXECUTION, DRAINER_METER);
counter_metric!(SHUTDOWN_SIGNAL_RECEIVED, DRAINER_METER);
counter_metric!(SUCCESSFUL_SHUTDOWN, DRAINER_METER);
counter_metric!(STREAM_EMPTY, DRAINER_METER);
counter_metric!(STREAM_PARSE_FAIL, DRAINER_METER);
counter_metric!(DRAINER_HEALTH, DRAINER_METER);

histogram_metric_f64!(QUERY_EXECUTION_TIME, DRAINER_METER); // Time in (ms) milliseconds
histogram_metric_f64!(REDIS_STREAM_READ_TIME, DRAINER_METER); // Time in (ms) milliseconds
histogram_metric_f64!(REDIS_STREAM_TRIM_TIME, DRAINER_METER); // Time in (ms) milliseconds
histogram_metric_f64!(CLEANUP_TIME, DRAINER_METER); // Time in (ms) milliseconds
histogram_metric_u64!(DRAINER_DELAY_SECONDS, DRAINER_METER); // Time in (s) seconds
histogram_metric_f64!(REDIS_STREAM_DEL_TIME, DRAINER_METER); // Time in (ms) milliseconds


// Function: consumer_error_handler
// File: crates/scheduler/src/consumer.rs
// Module: scheduler

pub fn consumer_error_handler(
    state: &(dyn SchedulerInterface + 'static)


// File: crates/router/src/types/api/verify_connector/paypal.rs
// Module: router

use error_stack::ResultExt;

use super::{VerifyConnector, VerifyConnectorData};
use crate::{
    connector,
    core::errors,
    routes::SessionState,
    services,
    types::{self, api},
};

#[async_trait::async_trait]
impl VerifyConnector for connector::Paypal {
    async fn get_access_token(
        state: &SessionState,
        connector_data: VerifyConnectorData,
    ) -> errors::CustomResult<Option<types::AccessToken>, errors::ApiErrorResponse> {
        let token_data: types::AccessTokenRequestData =
            connector_data.connector_auth.clone().try_into()?;
        let router_data = connector_data.get_router_data(state, token_data, None);

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

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

        match response {
            Ok(res) => Some(
                connector_data
                    .connector
                    .get_connector_integration()
                    .handle_response(&router_data, None, res)
                    .change_context(errors::ApiErrorResponse::InternalServerError)?
                    .response
                    .map_err(|_| errors::ApiErrorResponse::InternalServerError.into()),
            )
            .transpose(),
            Err(response_data) => {
                Self::handle_access_token_error_response::<
                    api::AccessTokenAuth,
                    types::AccessTokenFlowData,
                    types::AccessTokenRequestData,
                    types::AccessToken,
                >(
                    connector_data.connector.get_connector_integration(),
                    response_data,
                )
                .await
            }
        }
    }
}