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

pub mod transformers;

use std::{collections::HashMap, sync::LazyLock};

use api_models::webhooks::IncomingWebhookEvent;
use common_enums::{
    CallConnectorAction, CaptureMethod, PaymentAction, PaymentChargeType, PaymentMethodType,
    PaymentResourceUpdateStatus, StripeChargeType,
};
use common_utils::{
    crypto,
    errors::CustomResult,
    ext_traits::{ByteSliceExt as _, BytesExt},
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{
        AmountConvertor, MinorUnit, MinorUnitForConnector, StringMinorUnit,
        StringMinorUnitForConnector,
    },
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        AccessTokenAuth, Authorize, Capture, CreateConnectorCustomer, Evidence, Execute,
        IncrementalAuthorization, PSync, PaymentMethodToken, RSync, Retrieve, Session,
        SetupMandate, UpdateMetadata, Upload, Void,
    },
    router_request_types::{
        AccessTokenRequestData, ConnectorCustomerData, PaymentMethodTokenizationData,
        PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData,
        PaymentsIncrementalAuthorizationData, PaymentsSessionData, PaymentsSyncData,
        PaymentsUpdateMetadataData, RefundsData, RetrieveFileRequestData, SetupMandateRequestData,
        SplitRefundsRequest, SubmitEvidenceRequestData, UploadFileRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        RetrieveFileResponse, SubmitEvidenceResponse, SupportedPaymentMethods,
        SupportedPaymentMethodsExt, UploadFileResponse,
    },
    types::{
        ConnectorCustomerRouterData, PaymentsAuthorizeRouterData, PaymentsCancelRouterData,
        PaymentsCaptureRouterData, PaymentsIncrementalAuthorizationRouterData,
        PaymentsSyncRouterData, PaymentsUpdateMetadataRouterData, RefundsRouterData,
        TokenizationRouterData,
    },
};
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::{
    router_flow_types::{PoCancel, PoCreate, PoFulfill, PoRecipient, PoRecipientAccount},
    types::{PayoutsData, PayoutsResponseData, PayoutsRouterData},
};
#[cfg(feature = "payouts")]
use hyperswitch_interfaces::types::{
    PayoutCancelType, PayoutCreateType, PayoutFulfillType, PayoutRecipientAccountType,
    PayoutRecipientType,
};
use hyperswitch_interfaces::{
    api::{
        self,
        disputes::SubmitEvidence,
        files::{FilePurpose, FileUpload, RetrieveFile, UploadFile},
        ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorRedirectResponse,
        ConnectorSpecifications, ConnectorValidation, PaymentIncrementalAuthorization,
    },
    configs::Connectors,
    consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
    disputes::DisputePayload,
    errors::ConnectorError,
    events::connector_api_logs::ConnectorEvent,
    types::{
        ConnectorCustomerType, IncrementalAuthorizationType, PaymentsAuthorizeType,
        PaymentsCaptureType, PaymentsSyncType, PaymentsUpdateMetadataType, PaymentsVoidType,
        RefundExecuteType, RefundSyncType, Response, RetrieveFileType, SubmitEvidenceType,
        TokenizationType, UploadFileType,
    },
    webhooks::{IncomingWebhook, IncomingWebhookRequestDetails},
};
use masking::{Mask as _, Maskable, PeekInterface};
use router_env::{instrument, tracing};
use stripe::auth_headers;

use self::transformers as stripe;
#[cfg(feature = "payouts")]
use crate::utils::{PayoutsData as OtherPayoutsData, RouterData as OtherRouterData};
use crate::{
    constants::headers::{AUTHORIZATION, CONTENT_TYPE, STRIPE_COMPATIBLE_CONNECT_ACCOUNT},
    types::{
        ResponseRouterData, RetrieveFileRouterData, SubmitEvidenceRouterData, UploadFileRouterData,
    },
    utils::{
        self, get_authorise_integrity_object, get_capture_integrity_object,
        get_refund_integrity_object, get_sync_integrity_object, PaymentMethodDataType,
        RefundsRequestData as OtherRefundsRequestData,
    },
};
#[derive(Clone)]
pub struct Stripe {
    amount_converter: &'static (dyn AmountConvertor<Output = MinorUnit> + Sync),
    amount_converter_webhooks: &'static (dyn AmountConvertor<Output = StringMinorUnit> + Sync),
}

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

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Stripe
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let mut header = vec![(
            CONTENT_TYPE.to_string(),
            Self::common_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)
    }
}

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

    fn common_get_content_type(&self) -> &'static str {
        "application/x-www-form-urlencoded"
    }

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let auth = stripe::StripeAuthType::try_from(auth_type)
            .change_context(ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![
            (
                AUTHORIZATION.to_string(),
                format!("Bearer {}", auth.api_key.peek()).into_masked(),
            ),
            (
                auth_headers::STRIPE_API_VERSION.to_string(),
                auth_headers::STRIPE_VERSION.to_string().into_masked(),
            ),
        ])
    }

    #[cfg(feature = "payouts")]
    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        use hyperswitch_interfaces::consts::NO_ERROR_CODE;

        let response: stripe::StripeConnectErrorResponse = res
            .response
            .parse_struct("StripeConnectErrorResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_error_response_body(&response));
        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response
                .error
                .code
                .clone()
                .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
            message: response
                .error
                .code
                .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
            reason: response.error.message,
            attempt_status: None,
            connector_transaction_id: response.error.payment_intent.map(|pi| pi.id),
            network_advice_code: response.error.network_advice_code,
            network_decline_code: response.error.network_decline_code,
            network_error_message: response.error.decline_code.or(response.error.advice_code),
            connector_metadata: None,
        })
    }
}

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

    fn validate_mandate_payment(
        &self,
        pm_type: Option<PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), ConnectorError> {
        let mandate_supported_pmd = std::collections::HashSet::from([
            PaymentMethodDataType::Card,
            PaymentMethodDataType::ApplePay,
            PaymentMethodDataType::GooglePay,
            PaymentMethodDataType::AchBankDebit,
            PaymentMethodDataType::BacsBankDebit,
            PaymentMethodDataType::BecsBankDebit,
            PaymentMethodDataType::SepaBankDebit,
            PaymentMethodDataType::Sofort,
            PaymentMethodDataType::Ideal,
            PaymentMethodDataType::BancontactCard,
        ]);
        utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id())
    }
}

impl api::Payment for Stripe {}

impl api::PaymentAuthorize for Stripe {}
impl api::PaymentUpdateMetadata for Stripe {}
impl api::PaymentSync for Stripe {}
impl api::PaymentVoid for Stripe {}
impl api::PaymentCapture for Stripe {}
impl api::PaymentSession for Stripe {}
impl api::ConnectorAccessToken for Stripe {}

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

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Stripe {
    // Not Implemented (R)
}

impl api::ConnectorCustomer for Stripe {}

impl ConnectorIntegration<CreateConnectorCustomer, ConnectorCustomerData, PaymentsResponseData>
    for Stripe
{
    fn get_headers(
        &self,
        req: &ConnectorCustomerRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let mut header = vec![(
            CONTENT_TYPE.to_string(),
            ConnectorCustomerType::get_content_type(self)
                .to_string()
                .into(),
        )];
        if let Some(common_types::payments::SplitPaymentsRequest::StripeSplitPayment(
            stripe_split_payment,
        )) = &req.request.split_payments
        {
            if stripe_split_payment.charge_type
                == PaymentChargeType::Stripe(StripeChargeType::Direct)
            {
                let mut customer_account_header = vec![(
                    STRIPE_COMPATIBLE_CONNECT_ACCOUNT.to_string(),
                    stripe_split_payment
                        .transfer_account_id
                        .clone()
                        .into_masked(),
                )];
                header.append(&mut customer_account_header);
            }
        }
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

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

    fn get_url(
        &self,
        _req: &ConnectorCustomerRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}{}", self.base_url(connectors), "v1/customers"))
    }

    fn get_request_body(
        &self,
        req: &ConnectorCustomerRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let connector_req = stripe::CustomerRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &ConnectorCustomerRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<ConnectorCustomerRouterData, ConnectorError>
    where
        PaymentsResponseData: Clone,
    {
        let response: stripe::StripeCustomerResponse = res
            .response
            .parse_struct("StripeCustomerResponse")
            .change_context(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(ConnectorError::ResponseHandlingFailed)
    }

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response
                .error
                .code
                .clone()
                .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
            message: response
                .error
                .code
                .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
            reason: response.error.message.map(|message| {
                response
                    .error
                    .decline_code
                    .clone()
                    .map(|decline_code| {
                        format!("message - {message}, decline_code - {decline_code}")
                    })
                    .unwrap_or(message)
            }),
            attempt_status: None,
            connector_transaction_id: response.error.payment_intent.map(|pi| pi.id),
            network_advice_code: response.error.network_advice_code,
            network_decline_code: response.error.network_decline_code,
            network_error_message: response.error.decline_code.or(response.error.advice_code),
            connector_metadata: None,
        })
    }
}

impl api::PaymentToken for Stripe {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Stripe
{
    fn get_headers(
        &self,
        req: &TokenizationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let mut header = vec![(
            CONTENT_TYPE.to_string(),
            TokenizationType::get_content_type(self).to_string().into(),
        )];
        if let Some(common_types::payments::SplitPaymentsRequest::StripeSplitPayment(
            stripe_split_payment,
        )) = &req.request.split_payments
        {
            if stripe_split_payment.charge_type
                == PaymentChargeType::Stripe(StripeChargeType::Direct)
            {
                let mut customer_account_header = vec![(
                    STRIPE_COMPATIBLE_CONNECT_ACCOUNT.to_string(),
                    stripe_split_payment
                        .transfer_account_id
                        .clone()
                        .into_masked(),
                )];
                header.append(&mut customer_account_header);
            }
        }
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

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

    fn get_url(
        &self,
        req: &TokenizationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        if matches!(
            req.request.split_payments,
            Some(common_types::payments::SplitPaymentsRequest::StripeSplitPayment(_))
        ) {
            return Ok(format!(
                "{}{}",
                self.base_url(connectors),
                "v1/payment_methods"
            ));
        }
        Ok(format!("{}{}", self.base_url(connectors), "v1/tokens"))
    }

    fn get_request_body(
        &self,
        req: &TokenizationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let connector_req = stripe::TokenRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &TokenizationRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<TokenizationRouterData, ConnectorError>
    where
        PaymentsResponseData: Clone,
    {
        let response: stripe::StripeTokenResponse = res
            .response
            .parse_struct("StripeTokenResponse")
            .change_context(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(ConnectorError::ResponseHandlingFailed)
    }

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response
                .error
                .code
                .clone()
                .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
            message: response
                .error
                .code
                .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
            reason: response.error.message.map(|message| {
                response
                    .error
                    .decline_code
                    .clone()
                    .map(|decline_code| {
                        format!("message - {message}, decline_code - {decline_code}")
                    })
                    .unwrap_or(message)
            }),
            attempt_status: None,
            connector_transaction_id: response.error.payment_intent.map(|pi| pi.id),
            network_advice_code: response.error.network_advice_code,
            network_decline_code: response.error.network_decline_code,
            network_error_message: response.error.decline_code.or(response.error.advice_code),
            connector_metadata: None,
        })
    }
}

impl api::MandateSetup for Stripe {}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Stripe {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let mut header = vec![(
            CONTENT_TYPE.to_string(),
            Self::common_get_content_type(self).to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

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

    fn get_url(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        let id = req.request.connector_transaction_id.as_str();
        Ok(format!(
            "{}{}/{}/capture",
            self.base_url(connectors),
            "v1/payment_intents",
            id
        ))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_amount_to_capture,
            req.request.currency,
        )?;
        let connector_req = stripe::CaptureRequest::try_from(amount)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, ConnectorError>
    where
        PaymentsCaptureData: Clone,
        PaymentsResponseData: Clone,
    {
        let response: stripe::PaymentIntentResponse = res
            .response
            .parse_struct("PaymentIntentResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;

        let response_integrity_object = get_capture_integrity_object(
            self.amount_converter,
            response.amount_received,
            response.currency.clone(),
        )?;

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

        let new_router_data = RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(ConnectorError::ResponseHandlingFailed);

        new_router_data.map(|mut router_data| {
            router_data.request.integrity_object = Some(response_integrity_object);
            router_data
        })
    }

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response
                .error
                .code
                .clone()
                .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
            message: response
                .error
                .code
                .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
            reason: response.error.message.map(|message| {
                response
                    .error
                    .decline_code
                    .clone()
                    .map(|decline_code| {
                        format!("message - {message}, decline_code - {decline_code}")
                    })
                    .unwrap_or(message)
            }),
            attempt_status: None,
            connector_transaction_id: response.error.payment_intent.map(|pi| pi.id),
            network_advice_code: response.error.network_advice_code,
            network_decline_code: response.error.network_decline_code,
            network_error_message: response.error.decline_code.or(response.error.advice_code),
            connector_metadata: None,
        })
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Stripe {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let mut header = vec![(
            CONTENT_TYPE.to_string(),
            PaymentsSyncType::get_content_type(self).to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);

        if let Some(common_types::payments::SplitPaymentsRequest::StripeSplitPayment(
            stripe_split_payment,
        )) = &req.request.split_payments
        {
            transformers::transform_headers_for_connect_platform(
                stripe_split_payment.charge_type.clone(),
                stripe_split_payment.transfer_account_id.clone(),
                &mut header,
            );
        }
        Ok(header)
    }

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

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        let id = req.request.connector_transaction_id.clone();

        match id.get_connector_transaction_id() {
            Ok(x) if x.starts_with("set") => Ok(format!(
                "{}{}/{}?expand[0]=latest_attempt", // expand latest attempt to extract payment checks and three_d_secure data
                self.base_url(connectors),
                "v1/setup_intents",
                x,
            )),
            Ok(x) => Ok(format!(
                "{}{}/{}{}",
                self.base_url(connectors),
                "v1/payment_intents",
                x,
                "?expand[0]=latest_charge" //updated payment_id(if present) reside inside latest_charge field
            )),
            x => x.change_context(ConnectorError::MissingConnectorTransactionID),
        }
    }

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

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, ConnectorError>
    where
        PaymentsResponseData: Clone,
    {
        let id = data.request.connector_transaction_id.clone();
        match id.get_connector_transaction_id() {
            Ok(x) if x.starts_with("set") => {
                let response: stripe::SetupIntentResponse = res
                    .response
                    .parse_struct("SetupIntentSyncResponse")
                    .change_context(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,
                })
            }
            Ok(_) => {
                let response: stripe::PaymentIntentSyncResponse = res
                    .response
                    .parse_struct("PaymentIntentSyncResponse")
                    .change_context(ConnectorError::ResponseDeserializationFailed)?;

                let response_integrity_object = get_sync_integrity_object(
                    self.amount_converter,
                    response.amount,
                    response.currency.clone(),
                )?;

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

                let new_router_data = RouterData::try_from(ResponseRouterData {
                    response,
                    data: data.clone(),
                    http_code: res.status_code,
                });
                new_router_data.map(|mut router_data| {
                    router_data.request.integrity_object = Some(response_integrity_object);
                    router_data
                })
            }
            Err(err) => Err(err).change_context(ConnectorError::MissingConnectorTransactionID),
        }
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        let response: stripe::ErrorResponse = res
            .response
            .parse_struct("ErrorResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_error_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response
                .error
                .code
                .clone()
                .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
            message: response
                .error
                .code
                .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
            reason: response.error.message.map(|message| {
                response
                    .error
                    .decline_code
                    .clone()
                    .map(|decline_code| {
                        format!("message - {message}, decline_code - {decline_code}")
                    })
                    .unwrap_or(message)
            }),
            attempt_status: None,
            connector_transaction_id: response.error.payment_intent.map(|pi| pi.id),
            network_advice_code: response.error.network_advice_code,
            network_decline_code: response.error.network_decline_code,
            network_error_message: response.error.decline_code.or(response.error.advice_code),
            connector_metadata: None,
        })
    }
}

#[async_trait::async_trait]
impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Stripe {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        let mut header = vec![(
            CONTENT_TYPE.to_string(),
            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);

        let stripe_split_payment_metadata = stripe::StripeSplitPaymentRequest::try_from(req)?;

        // if the request has split payment object, then append the transfer account id in headers in charge_type is Direct
        if let Some(common_types::payments::SplitPaymentsRequest::StripeSplitPayment(
            stripe_split_payment,
        )) = &req.request.split_payments
        {
            if stripe_split_payment.charge_type
                == PaymentChargeType::Stripe(StripeChargeType::Direct)
            {
                let mut customer_account_header = vec![(
                    STRIPE_COMPATIBLE_CONNECT_ACCOUNT.to_string(),
                    stripe_split_payment
                        .transfer_account_id
                        .clone()
                        .into_masked(),
                )];
                header.append(&mut customer_account_header);
            }
        }
        // if request doesn't have transfer_account_id, but stripe_split_payment_metadata has it, append it
        else if let Some(transfer_account_id) =
            stripe_split_payment_metadata.transfer_account_id.clone()
        {
            let mut customer_account_header = vec![(
                STRIPE_COMPATIBLE_CONNECT_ACCOUNT.to_string(),
                transfer_account_id.into_masked(),
            )];
            header.append(&mut customer_account_header);
        }
        Ok(header)
    }

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

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!(
            "{}{}",
            self.base_url(connectors),
            "v1/payment_intents"
        ))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;
        let connector_req = stripe::PaymentIntentRequest::try_from((req, amount))?;

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

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

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

        let response_integrity_object = get_authorise_integrity_object(
            self.amount_converter,
            response.amount,
            response.currency.clone(),
        )?;

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

        let new_router_data = RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
        .change_context(ConnectorError::ResponseHandlingFailed);

        new_router_data.map(|mut router_data| {
            router_data.request.integrity_object = Some(response_integrity_object);
            router_data
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        let response: stripe::ErrorResponse = res
            .response
            .parse_struct("ErrorResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_error_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response
                .error
                .code
                .clone()
                .unwrap_or_else(|| NO_ERROR_CODE.to_string()),
            message: response
                .error
                .code
                .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()),
            reason: response.error.message.map(|message| {
                response
                    .error
                    .decline_code
                    .clone()
                    .map(|decline_code| {
                        format!("message - {message}, decline_code - {decline_code}")
                    })
                    .unwrap_or(message)
            }),
            attempt_status: None,
            connector_transaction_id: response.error.payment_intent.map(|pi| pi.id),
            network_advice_code: response.error.network_advice_code,
            network_decline_code: response.error.network_decline_code,
            network_error_message: response.error.decline_code.or(response.error.advice_code),
            connector_metadata: None,
        })
    }
}

impl PaymentIncrementalAuthorization for Stripe {}

impl
    ConnectorIntegration<
        IncrementalAuthorization,
        PaymentsIncrementalAuthorizationData,
        PaymentsResponseData,
    > for Stripe
{
    fn get_headers(
        &self,
        req: &PaymentsIncrementalAuthorizationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_http_method(&self) -> Method {
        Method::Post
    }

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

    fn get_url(
        &self,
        req: &PaymentsIncrementalAuthorizationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!(
            "{}v1/payment_intents/{}/increment_authorization",
            self.base_url(connectors),
            req.request.connector_transaction_id,
        ))
    }

    fn get_request_body(
        &self,
        req: &PaymentsIncrementalAuthorizationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            MinorUnit::new(req.request.total_amount),
            req.request.currency,
        )?;
        let connector_req = stripe::StripeIncrementalAuthRequest { amount }; // Incremental authorization can be done a maximum of 10 times in Stripe

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

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

    fn handle_response(
        &self,
        data: &PaymentsIncrementalAuthorizationRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<
        RouterData<
            IncrementalAuthorization,
            PaymentsIncrementalAuthorizationData,
            PaymentsResponseData,
        >,
        ConnectorError,
    > {
        let response: stripe::PaymentIntentResponse = res
            .response
            .parse_struct("PaymentIntentResponse")


pub fn server(state: AppState) -> Scope {
        web::scope("/v2/merchant-accounts")
            .app_data(web::Data::new(state))
            .service(web::resource("").route(web::post().to(admin::merchant_account_create)))
            .service(
                web::scope("/{id}")
                    .service(
                        web::resource("")
                            .route(web::get().to(admin::retrieve_merchant_account))
                            .route(web::put().to(admin::update_merchant_account)),
                    )
                    .service(
                        web::resource("/profiles").route(web::get().to(profiles::profiles_list)),
                    )
                    .service(
                        web::resource("/kv")
                            .route(web::post().to(admin::merchant_account_toggle_kv))
                            .route(web::get().to(admin::merchant_account_kv_status)),
                    ),
            )
    }


OpenAPI Block Path: components.schemas.ElementPosition
{
  "type": "string",
  "enum": [
    "left",
    "top left",
    "top",
    "top right",
    "right",
    "bottom right",
    "bottom",
    "bottom left",
    "center"
  ]
}


pub struct HateoasLink {
    pub href: String,
    pub rel: String,
    pub method: String,
}


impl ChargebeeCustomer {
    // the logic to find connector customer id & mandate id is different for different gateways, reference : https://apidocs.chargebee.com/docs/api/customers?prod_cat_ver=2#customer_payment_method_reference_id .
    pub fn find_connector_ids(&self) -> Result<ChargebeeMandateDetails, errors::ConnectorError> {
        match self.payment_method.gateway {
            ChargebeeGateway::Stripe | ChargebeeGateway::Braintree => {
                let mut parts = self.payment_method.reference_id.split('/');
                let customer_id = parts
                    .next()
                    .ok_or(errors::ConnectorError::WebhookBodyDecodingFailed)?
                    .to_string();
                let mandate_id = parts
                    .next_back()
                    .ok_or(errors::ConnectorError::WebhookBodyDecodingFailed)?
                    .to_string();
                Ok(ChargebeeMandateDetails {
                    customer_id,
                    mandate_id,
                })
            }
        }
    }
}


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


OpenAPI Block Path: components.schemas.CaptureResponse
{
  "type": "object",
  "required": [
    "capture_id",
    "status",
    "amount",
    "connector",
    "authorized_attempt_id",
    "capture_sequence"
  ],
  "properties": {
    "capture_id": {
      "type": "string",
      "description": "A unique identifier for this specific capture operation."
    },
    "status": {
      "$ref": "#/components/schemas/CaptureStatus"
    },
    "amount": {
      "type": "integer",
      "format": "int64",
      "description": "The capture amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,",
      "example": 6540
    },
    "currency": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Currency"
        }
      ],
      "nullable": true
    },
    "connector": {
      "type": "string",
      "description": "The name of the payment connector that processed this capture."
    },
    "authorized_attempt_id": {
      "type": "string",
      "description": "The ID of the payment attempt that was successfully authorized and subsequently captured by this operation."
    },
    "connector_capture_id": {
      "type": "string",
      "description": "A unique identifier for this capture provided by the connector",
      "nullable": true
    },
    "capture_sequence": {
      "type": "integer",
      "format": "int32",
      "description": "Sequence number of this capture, in the series of captures made for the parent attempt"
    },
    "error_message": {
      "type": "string",
      "description": "A human-readable message from the connector explaining why this capture operation failed, if applicable.",
      "nullable": true
    },
    "error_code": {
      "type": "string",
      "description": "The error code returned by the connector if this capture operation failed. This code is connector-specific.",
      "nullable": true
    },
    "error_reason": {
      "type": "string",
      "description": "A more detailed reason from the connector explaining the capture failure, if available.",
      "nullable": true
    },
    "reference_id": {
      "type": "string",
      "description": "The connector's own reference or transaction ID for this specific capture operation. Useful for reconciliation.",
      "nullable": true
    }
  }
}


pub struct ItaubankRefundRequest {
    pub valor: StringMajorUnit, // refund_amount
}


pub struct ConfigUpdateInternal {
    config: Option<String>,
}


pub async fn populate_bin_details_for_payment_method_create(
    card_details: api_models::payment_methods::CardDetail,
    db: Box<dyn state::PaymentMethodsStorageInterface>,
) -> api_models::payment_methods::CardDetail {
    let card_isin: Option<_> = Some(card_details.card_number.get_card_isin());
    if card_details.card_issuer.is_some()
        && card_details.card_network.is_some()
        && card_details.card_type.is_some()
        && card_details.card_issuing_country.is_some()
    {
        api::CardDetail {
            card_issuer: card_details.card_issuer.to_owned(),
            card_network: card_details.card_network.clone(),
            card_type: card_details.card_type.to_owned(),
            card_issuing_country: card_details.card_issuing_country.to_owned(),
            card_exp_month: card_details.card_exp_month.clone(),
            card_exp_year: card_details.card_exp_year.clone(),
            card_holder_name: card_details.card_holder_name.clone(),
            card_number: card_details.card_number.clone(),
            nick_name: card_details.nick_name.clone(),
        }
    } else {
        let card_info = card_isin
            .clone()
            .async_and_then(|card_isin| async move {
                db.get_card_info(&card_isin)
                    .await
                    .map_err(|error| logger::error!(card_info_error=?error))
                    .ok()
            })
            .await
            .flatten()
            .map(|card_info| api::CardDetail {
                card_issuer: card_info.card_issuer,
                card_network: card_info.card_network.clone(),
                card_type: card_info.card_type,
                card_issuing_country: card_info.card_issuing_country,
                card_exp_month: card_details.card_exp_month.clone(),
                card_exp_year: card_details.card_exp_year.clone(),
                card_holder_name: card_details.card_holder_name.clone(),
                card_number: card_details.card_number.clone(),
                nick_name: card_details.nick_name.clone(),
            });
        card_info.unwrap_or_else(|| api::CardDetail {
            card_issuer: None,
            card_network: None,
            card_type: None,
            card_issuing_country: None,
            card_exp_month: card_details.card_exp_month.clone(),
            card_exp_year: card_details.card_exp_year.clone(),
            card_holder_name: card_details.card_holder_name.clone(),
            card_number: card_details.card_number.clone(),
            nick_name: card_details.nick_name.clone(),
        })
    }
}


/// Custom deserializer for output from decision_engine, this is required as untagged enum is
/// stored but the enum requires tagged deserialization, hence deserializing it into specific
/// variants
pub fn extract_de_output_connectors(
    output_value: serde_json::Value,
) -> RoutingResult<Vec<ConnectorInfo>> {
    const SINGLE: &str = "straight_through";
    const PRIORITY: &str = "priority";
    const VOLUME_SPLIT: &str = "volume_split";
    const VOLUME_SPLIT_PRIORITY: &str = "volume_split_priority";

    let obj = output_value.as_object().ok_or_else(|| {
        logger::error!("decision_engine_euclid_error: output is not a JSON object");
        errors::RoutingError::OpenRouterError("Expected output to be a JSON object".into())
    })?;

    let type_str = obj.get("type").and_then(|v| v.as_str()).ok_or_else(|| {
        logger::error!("decision_engine_euclid_error: missing or invalid 'type' in output");
        errors::RoutingError::OpenRouterError("Missing or invalid 'type' field in output".into())
    })?;

    match type_str {
        SINGLE => {
            let connector_value = obj.get("connector").ok_or_else(|| {
                logger::error!(
                    "decision_engine_euclid_error: missing 'connector' field for type=single"
                );
                errors::RoutingError::OpenRouterError(
                    "Missing 'connector' field for single output".into(),
                )
            })?;
            let connector: ConnectorInfo = serde_json::from_value(connector_value.clone())
                .map_err(|e| {
                    logger::error!(
                        ?e,
                        "decision_engine_euclid_error: Failed to parse single connector"
                    );
                    errors::RoutingError::OpenRouterError(
                        "Failed to deserialize single connector".into(),
                    )
                })?;
            Ok(vec![connector])
        }

        PRIORITY => {
            let connectors_value = obj.get("connectors").ok_or_else(|| {
                logger::error!(
                    "decision_engine_euclid_error: missing 'connectors' field for type=priority"
                );
                errors::RoutingError::OpenRouterError(
                    "Missing 'connectors' field for priority output".into(),
                )
            })?;
            let connectors: Vec<ConnectorInfo> = serde_json::from_value(connectors_value.clone())
                .map_err(|e| {
                logger::error!(
                    ?e,
                    "decision_engine_euclid_error: Failed to parse connectors for priority"
                );
                errors::RoutingError::OpenRouterError(
                    "Failed to deserialize priority connectors".into(),
                )
            })?;
            Ok(connectors)
        }

        VOLUME_SPLIT => {
            let splits_value = obj.get("splits").ok_or_else(|| {
                logger::error!(
                    "decision_engine_euclid_error: missing 'splits' field for type=volume_split"
                );
                errors::RoutingError::OpenRouterError(
                    "Missing 'splits' field for volume_split output".into(),
                )
            })?;

            // Transform each {connector, split} into {output, split}
            let fixed_splits: Vec<_> = splits_value
                .as_array()
                .ok_or_else(|| {
                    logger::error!("decision_engine_euclid_error: 'splits' is not an array");
                    errors::RoutingError::OpenRouterError("'splits' field must be an array".into())
                })?
                .iter()
                .map(|entry| {
                    let mut entry_map = entry.as_object().cloned().ok_or_else(|| {
                        logger::error!(
                            "decision_engine_euclid_error: invalid split entry in volume_split"
                        );
                        errors::RoutingError::OpenRouterError(
                            "Invalid entry in splits array".into(),
                        )
                    })?;
                    if let Some(connector) = entry_map.remove("connector") {
                        entry_map.insert("output".to_string(), connector);
                    }
                    Ok::<_, error_stack::Report<errors::RoutingError>>(serde_json::Value::Object(
                        entry_map,
                    ))
                })
                .collect::<Result<Vec<_>, _>>()?;

            let splits: Vec<VolumeSplit<ConnectorInfo>> =
                serde_json::from_value(serde_json::Value::Array(fixed_splits)).map_err(|e| {
                    logger::error!(
                        ?e,
                        "decision_engine_euclid_error: Failed to parse volume_split"
                    );
                    errors::RoutingError::OpenRouterError(
                        "Failed to deserialize volume_split connectors".into(),
                    )
                })?;

            Ok(splits.into_iter().map(|s| s.output).collect())
        }

        VOLUME_SPLIT_PRIORITY => {
            let splits_value = obj.get("splits").ok_or_else(|| {
                logger::error!("decision_engine_euclid_error: missing 'splits' field for type=volume_split_priority");
                errors::RoutingError::OpenRouterError("Missing 'splits' field for volume_split_priority output".into())
            })?;

            // Transform each {connector: [...], split} into {output: [...], split}
            let fixed_splits: Vec<_> = splits_value
                .as_array()
                .ok_or_else(|| {
                    logger::error!("decision_engine_euclid_error: 'splits' is not an array");
                    errors::RoutingError::OpenRouterError("'splits' field must be an array".into())
                })?
                .iter()
                .map(|entry| {
                    let mut entry_map = entry.as_object().cloned().ok_or_else(|| {
                        logger::error!("decision_engine_euclid_error: invalid split entry in volume_split_priority");
                        errors::RoutingError::OpenRouterError("Invalid entry in splits array".into())
                    })?;
                    if let Some(connector) = entry_map.remove("connector") {
                        entry_map.insert("output".to_string(), connector);
                    }
                    Ok::<_, error_stack::Report<errors::RoutingError>>(serde_json::Value::Object(entry_map))
                })
                .collect::<Result<Vec<_>, _>>()?;

            let splits: Vec<VolumeSplit<Vec<ConnectorInfo>>> =
                serde_json::from_value(serde_json::Value::Array(fixed_splits)).map_err(|e| {
                    logger::error!(
                        ?e,
                        "decision_engine_euclid_error: Failed to parse volume_split_priority"
                    );
                    errors::RoutingError::OpenRouterError(
                        "Failed to deserialize volume_split_priority connectors".into(),
                    )
                })?;

            Ok(splits.into_iter().flat_map(|s| s.output).collect())
        }

        other => {
            logger::error!(type_str=%other, "decision_engine_euclid_error: unknown output type");
            Err(
                errors::RoutingError::OpenRouterError(format!("Unknown output type: {other}"))
                    .into(),
            )
        }
    }
}


pub struct SilverflowAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) api_secret: Secret<String>,
    pub(super) merchant_acceptor_key: Secret<String>,
}


impl api::ConnectorAccessToken for Paypal {}

impl api::ConnectorAccessToken for for Paypal


impl<T: KafkaMessage> KafkaMessage for KafkaEvent<'_, T> {
    fn key(&self) -> String {
        self.event.key()
    }

    fn event_type(&self) -> EventType {
        self.event.event_type()
    }

    fn creation_timestamp(&self) -> Option<i64> {
        self.event.creation_timestamp()
    }
}


pub fn build(self) -> api::CardNetworkTokenizeResponse {
        api::CardNetworkTokenizeResponse {
            payment_method_response: self.payment_method_response,
            customer: self.customer.cloned(),
            card_tokenized: self.card_tokenized,
            error_code: self.error_code.cloned(),
            error_message: self.error_message.cloned(),
            // Below field is mutated by caller functions for batched API operations
            tokenization_data: None,
        }
    }


impl api::PaymentSync for Wellsfargopayout {}

impl api::PaymentSync for for Wellsfargopayout


pub struct RiskifiedPaymentsCheckoutRequest {
    order: CheckoutRequest,
}


pub struct ApiEventMetricsBucketIdentifier {
    #[serde(rename = "time_range")]
    pub time_bucket: TimeRange,
    // Coz FE sucks
    #[serde(rename = "time_bucket")]
    #[serde(with = "common_utils::custom_serde::iso8601custom")]
    pub start_time: time::PrimitiveDateTime,
}


pub fn check_payment_link_status(
    payment_link_expiry: PrimitiveDateTime,
) -> api_models::payments::PaymentLinkStatus {
    let curr_time = common_utils::date_time::now();

    if curr_time > payment_link_expiry {
        api_models::payments::PaymentLinkStatus::Expired
    } else {
        api_models::payments::PaymentLinkStatus::Active
    }
}


pub async fn create_access_token<F: Clone + 'static>(
    state: &SessionState,
    connector_data: &api_types::ConnectorData,
    merchant_context: &domain::MerchantContext,
    router_data: &mut types::PayoutsRouterData<F>,
    payout_type: Option<enums::PayoutType>,
) -> RouterResult<()> {
    let connector_access_token = add_access_token_for_payout(
        state,
        connector_data,
        merchant_context,
        router_data,
        payout_type,
    )
    .await?;

    if connector_access_token.connector_supports_access_token {
        match connector_access_token.access_token_result {
            Ok(access_token) => {
                router_data.access_token = access_token;
            }
            Err(connector_error) => {
                router_data.response = Err(connector_error);
            }
        }
    }

    Ok(())
}


pub struct FacilitapayAuthResponse {
    username: Secret<String>,
    name: Secret<String>,
    pub jwt: Secret<String>,
}


pub struct DigitalvirgoRefundRequest {
    pub amount: FloatMajorUnit,
}


pub struct EliminationInformationEventResponse {
    pub entity: Option<BucketInformationEventResponse>,
    pub global: Option<BucketInformationEventResponse>,
}


pub struct SyncResponseBody {
    consultaoperacionesresponse: ConsultaOperacionesResponse,
}


impl RetrieveFile for Adyen {}


File: crates/router/src/core/payments/operations/payment_update_metadata.rs
 Public structs: 1

use std::marker::PhantomData;

use api_models::enums::FrmSuggestion;
use async_trait::async_trait;
use common_utils::types::keymanager::KeyManagerState;
use error_stack::ResultExt;
use masking::ExposeInterface;
use router_derive::PaymentOperation;
use router_env::{instrument, tracing};

use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
use crate::{
    core::{
        errors::{self, RouterResult, StorageErrorExt},
        payments::{self, helpers, operations, PaymentData},
    },
    routes::{app::ReqState, SessionState},
    services,
    types::{
        api::{self, PaymentIdTypeExt},
        domain,
        storage::{self, enums as storage_enums},
    },
    utils::OptionExt,
};

#[derive(Debug, Clone, Copy, PaymentOperation)]
#[operation(operations = "all", flow = "update_metadata")]
pub struct PaymentUpdateMetadata;

type PaymentUpdateMetadataOperation<'b, F> =
    BoxedOperation<'b, F, api::PaymentsUpdateMetadataRequest, PaymentData<F>>;

#[async_trait]
impl<F: Send + Clone + Sync> GetTracker<F, PaymentData<F>, api::PaymentsUpdateMetadataRequest>
    for PaymentUpdateMetadata
{
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &api::PaymentIdType,
        request: &api::PaymentsUpdateMetadataRequest,
        merchant_context: &domain::MerchantContext,
        _auth_flow: services::AuthFlow,
        _header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<
        operations::GetTrackerResponse<'a, F, api::PaymentsUpdateMetadataRequest, PaymentData<F>>,
    > {
        let payment_id = payment_id
            .get_payment_intent_id()
            .change_context(errors::ApiErrorResponse::PaymentNotFound)?;

        let db = &*state.store;
        let key_manager_state: &KeyManagerState = &state.into();
        let merchant_id = merchant_context.get_merchant_account().get_id();
        let storage_scheme = merchant_context.get_merchant_account().storage_scheme;
        let mut payment_intent = db
            .find_payment_intent_by_payment_id_merchant_id(
                &state.into(),
                &payment_id,
                merchant_id,
                merchant_context.get_merchant_key_store(),
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

        helpers::validate_payment_status_against_allowed_statuses(
            payment_intent.status,
            &[
                storage_enums::IntentStatus::Succeeded,
                storage_enums::IntentStatus::Failed,
                storage_enums::IntentStatus::PartiallyCaptured,
                storage_enums::IntentStatus::PartiallyCapturedAndCapturable,
                storage_enums::IntentStatus::RequiresCapture,
            ],
            "update_metadata",
        )?;

        let payment_attempt = db
            .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_intent.payment_id,
                merchant_id,
                payment_intent.active_attempt.get_id().as_str(),
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
        let currency = payment_intent.currency.get_required_value("currency")?;
        let amount = payment_attempt.get_total_amount().into();
        let profile_id = payment_intent
            .profile_id
            .as_ref()
            .get_required_value("profile_id")
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("'profile_id' not set in payment intent")?;

        let business_profile = db
            .find_business_profile_by_profile_id(
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                profile_id,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
                id: profile_id.get_string_repr().to_owned(),
            })?;

        let merged_metadata = payment_intent
            .merge_metadata(request.metadata.clone().expose())
            .change_context(errors::ApiErrorResponse::InvalidRequestData {
                message: "Metadata should be an object and contain at least 1 key".to_owned(),
            })?;

        payment_intent.metadata = Some(merged_metadata);

        let payment_data = PaymentData {
            flow: PhantomData,
            payment_intent,
            payment_attempt,
            currency,
            amount,
            email: None,
            mandate_id: None,
            mandate_connector: None,
            customer_acceptance: None,
            token: None,
            token_data: None,
            setup_mandate: None,
            address: payments::PaymentAddress::new(None, None, None, None),
            confirm: None,
            payment_method_data: None,
            payment_method_token: None,
            payment_method_info: None,
            force_sync: None,
            all_keys_required: None,
            refunds: vec![],
            disputes: vec![],
            attempts: None,
            sessions_token: vec![],
            card_cvc: None,
            creds_identifier: None,
            pm_token: None,
            connector_customer_id: None,
            recurring_mandate_payment_data: None,
            ephemeral_key: None,
            multiple_capture_data: None,
            redirect_response: None,
            surcharge_details: None,
            frm_message: None,
            payment_link_data: None,
            incremental_authorization_details: None,
            authorizations: vec![],
            authentication: None,
            recurring_details: None,
            poll_config: None,
            tax_data: None,
            session_id: None,
            service_details: None,
            card_testing_guard_data: None,
            vault_operation: None,
            threeds_method_comp_ind: None,
            whole_connector_response: None,
        };
        let get_trackers_response = operations::GetTrackerResponse {
            operation: Box::new(self),
            customer_details: None,
            payment_data,
            business_profile,
            mandate_type: None,
        };

        Ok(get_trackers_response)
    }
}

#[async_trait]
impl<F: Clone + Send + Sync> Domain<F, api::PaymentsUpdateMetadataRequest, PaymentData<F>>
    for PaymentUpdateMetadata
{
    #[instrument(skip_all)]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut PaymentData<F>,
        _request: Option<payments::CustomerDetails>,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> errors::CustomResult<
        (
            PaymentUpdateMetadataOperation<'a, F>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        Ok((Box::new(self), None))
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut PaymentData<F>,
        _storage_scheme: storage_enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        PaymentUpdateMetadataOperation<'a, F>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    async fn get_connector<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        state: &SessionState,
        _request: &api::PaymentsUpdateMetadataRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> errors::CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut PaymentData<F>,
    ) -> errors::CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[async_trait]
impl<F: Clone + Sync> UpdateTracker<F, PaymentData<F>, api::PaymentsUpdateMetadataRequest>
    for PaymentUpdateMetadata
{
    #[instrument(skip_all)]
    async fn update_trackers<'b>(
        &'b self,
        _state: &'b SessionState,
        _req_state: ReqState,
        payment_data: PaymentData<F>,
        _customer: Option<domain::Customer>,
        _storage_scheme: storage_enums::MerchantStorageScheme,
        _updated_customer: Option<storage::CustomerUpdate>,
        _key_store: &domain::MerchantKeyStore,
        _frm_suggestion: Option<FrmSuggestion>,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(PaymentUpdateMetadataOperation<'b, F>, PaymentData<F>)>
    where
        F: 'b + Send,
    {
        Ok((Box::new(self), payment_data))
    }
}

impl<F: Send + Clone + Sync> ValidateRequest<F, api::PaymentsUpdateMetadataRequest, PaymentData<F>>
    for PaymentUpdateMetadata
{
    #[instrument(skip_all)]
    fn validate_request<'a, 'b>(
        &'b self,
        request: &api::PaymentsUpdateMetadataRequest,
        merchant_context: &'a domain::MerchantContext,
    ) -> RouterResult<(
        PaymentUpdateMetadataOperation<'b, F>,
        operations::ValidateResult,
    )> {
        //payment id is already generated and should be sent in the request
        let given_payment_id = request.payment_id.clone();

        Ok((
            Box::new(self),
            operations::ValidateResult {
                merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
                payment_id: api::PaymentIdType::PaymentIntentId(given_payment_id),
                storage_scheme: merchant_context.get_merchant_account().storage_scheme,
                requeue: false,
            },
        ))
    }
}


pub async fn get_payment_filter_for_dimension<T>(
    dimension: PaymentDimensions,
    auth: &AuthInfo,
    time_range: &TimeRange,
    pool: &T,
) -> FiltersResult<Vec<PaymentFilterRow>>
where
    T: AnalyticsDataSource + PaymentFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::Payment);

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

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

    query_builder.set_distinct();

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


pub struct TypedSql {
    #[serde(flatten)]
    pub op: DBOperation,
}


pub struct SignifydAuthType {
    pub api_key: Secret<String>,
}


pub struct Void {
    #[serde(rename = "@id")]
    pub id: String,
    #[serde(rename = "@reportGroup")]
    pub report_group: String,
    pub cnp_txn_id: String,
}


pub fn get_shipping_cost(&self) -> Option<MinorUnit> {
        self.shipping_cost
    }


pub async fn get_theme_using_theme_id(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<String>,
) -> HttpResponse {
    let flow = Flow::GetThemeUsingThemeId;
    let payload = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, payload, _| theme_core::get_theme_using_theme_id(state, payload),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


pub struct BankAccountIban {
    pub account_holder_name: Secret<String>,
    pub iban: Option<Secret<String>>,
}


pub struct RecurlyWebhookBody {
    // Transaction uuid
    pub uuid: String,
    pub event_type: RecurlyPaymentEventType,
}


pub struct AuthenticationErrorMessage;


impl api::PaymentCapture for Trustpay {}

impl api::PaymentCapture for for Trustpay


impl AddressStructForDbUpdate<'_> {
    async fn update_address_if_sent(
        &self,
        db: &dyn StorageInterface,
    ) -> errors::CustomResult<Option<domain::Address>, errors::CustomersErrorResponse> {
        let address = if let Some(addr) = &self.update_customer.address {
            match self.domain_customer.address_id.clone() {
                Some(address_id) => {
                    let customer_address: api_models::payments::AddressDetails = addr.clone();
                    let update_address = self
                        .update_customer
                        .get_address_update(
                            self.state,
                            customer_address,
                            self.key_store.key.get_inner().peek(),
                            self.merchant_account.storage_scheme,
                            self.merchant_account.get_id().clone(),
                        )
                        .await
                        .switch()
                        .attach_printable("Failed while encrypting Address while Update")?;
                    Some(
                        db.update_address(
                            self.key_manager_state,
                            address_id,
                            update_address,
                            self.key_store,
                        )
                        .await
                        .switch()
                        .attach_printable(format!(
                            "Failed while updating address: merchant_id: {:?}, customer_id: {:?}",
                            self.merchant_account.get_id(),
                            self.domain_customer.customer_id
                        ))?,
                    )
                }
                None => {
                    let customer_address: api_models::payments::AddressDetails = addr.clone();

                    let address = self
                        .update_customer
                        .get_domain_address(
                            self.state,
                            customer_address,
                            self.merchant_account.get_id(),
                            &self.domain_customer.customer_id,
                            self.key_store.key.get_inner().peek(),
                            self.merchant_account.storage_scheme,
                        )
                        .await
                        .switch()
                        .attach_printable("Failed while encrypting address")?;
                    Some(
                        db.insert_address_for_customers(
                            self.key_manager_state,
                            address,
                            self.key_store,
                        )
                        .await
                        .switch()
                        .attach_printable("Failed while inserting new address")?,
                    )
                }
            }
        } else {
            match &self.domain_customer.address_id {
                Some(address_id) => Some(
                    db.find_address_by_address_id(
                        self.key_manager_state,
                        address_id,
                        self.key_store,
                    )
                    .await
                    .switch()?,
                ),
                None => None,
            }
        };
        Ok(address)
    }
}


pub struct SepaAndBacsBillingDetails {
    /// The Email ID for SEPA and BACS billing
    pub email: Email,
    /// The billing name for SEPA and BACS billing
    pub name: Secret<String>,
}


pub struct FiservRefundRequest {
    amount: Amount,
    merchant_details: MerchantDetails,
    reference_transaction_details: ReferenceTransactionDetails,
}


pub struct ToggleAllKVResponse {
    ///Total number of updated merchants
    #[schema(example = 20)]
    pub total_updated: usize,
    /// Status of KV for the specific merchant
    #[schema(example = true)]
    pub kv_enabled: bool,
}


pub async fn make_fulfillment_api_call(
    db: &dyn StorageInterface,
    fraud_check: FraudCheck,
    payment_intent: PaymentIntent,
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: frm_core_types::FrmFulfillmentRequest,
) -> RouterResponse<frm_types::FraudCheckResponseData> {
    let payment_attempt = db
        .find_payment_attempt_by_attempt_id_merchant_id(
            &payment_intent.active_attempt.get_id(),
            merchant_context.get_merchant_account().get_id(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::PaymentNotFound)?;
    let connector_data = FraudCheckConnectorData::get_connector_by_name(&fraud_check.frm_name)?;
    let connector_integration: services::BoxedFrmConnectorIntegrationInterface<
        Fulfillment,
        frm_types::FraudCheckFulfillmentData,
        frm_types::FraudCheckResponseData,
    > = connector_data.connector.get_connector_integration();
    let router_data = frm_flows::fulfillment_flow::construct_fulfillment_router_data(
        &state,
        &payment_intent,
        &payment_attempt,
        &merchant_context,
        fraud_check.frm_name.clone(),
        req,
    )
    .await?;
    let response = services::execute_connector_processing_step(
        &state,
        connector_integration,
        &router_data,
        payments::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await
    .to_payment_failed_response()?;
    let fraud_check_copy = fraud_check.clone();
    let fraud_check_update = FraudCheckUpdate::ResponseUpdate {
        frm_status: fraud_check.frm_status,
        frm_transaction_id: fraud_check.frm_transaction_id,
        frm_reason: fraud_check.frm_reason,
        frm_score: fraud_check.frm_score,
        metadata: fraud_check.metadata,
        modified_at: common_utils::date_time::now(),
        last_step: FraudCheckLastStep::Fulfillment,
        payment_capture_method: fraud_check.payment_capture_method,
    };
    let _updated = db
        .update_fraud_check_response_with_attempt_id(fraud_check_copy, fraud_check_update)
        .await
        .map_err(|error| error.change_context(errors::ApiErrorResponse::PaymentNotFound))?;
    let fulfillment_response =
        response
            .response
            .map_err(|err| errors::ApiErrorResponse::ExternalConnectorError {
                code: err.code,
                message: err.message,
                connector: connector_data.connector_name.clone().to_string(),
                status_code: err.status_code,
                reason: err.reason,
            })?;
    Ok(services::ApplicationResponse::Json(fulfillment_response))
}


File: crates/connector_configs/src/common_config.rs
 Public structs: 12

use api_models::{payment_methods, payments};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, serde::Serialize, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ZenApplePay {
    pub terminal_uuid: Option<String>,
    pub pay_wall_secret: Option<String>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, serde::Serialize, Clone)]
#[serde(untagged)]
pub enum ApplePayData {
    ApplePay(payments::ApplePayMetadata),
    ApplePayCombined(payments::ApplePayCombinedMetadata),
    Zen(ZenApplePay),
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct GpayDashboardPayLoad {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub gateway_merchant_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none", rename = "stripe:version")]
    pub stripe_version: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename(
        serialize = "stripe_publishable_key",
        deserialize = "stripe:publishable_key"
    ))]
    #[serde(alias = "stripe:publishable_key")]
    #[serde(alias = "stripe_publishable_key")]
    pub stripe_publishable_key: Option<String>,
    pub merchant_name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub merchant_id: Option<String>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, serde::Serialize, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ZenGooglePay {
    pub terminal_uuid: Option<String>,
    pub pay_wall_secret: Option<String>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, serde::Serialize, Clone)]
#[serde(untagged)]
pub enum GooglePayData {
    Standard(GpayDashboardPayLoad),
    Zen(ZenGooglePay),
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaypalSdkData {
    pub client_id: Option<String>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, serde::Serialize, Clone)]
#[serde(untagged)]
pub enum GoogleApiModelData {
    Standard(payments::GpayMetaData),
    Zen(ZenGooglePay),
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub struct PaymentMethodsEnabled {
    pub payment_method: api_models::enums::PaymentMethod,
    pub payment_method_types: Option<Vec<payment_methods::RequestPaymentMethodTypes>>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub struct ApiModelMetaData {
    pub merchant_config_currency: Option<api_models::enums::Currency>,
    pub merchant_account_id: Option<String>,
    pub account_name: Option<String>,
    pub terminal_id: Option<String>,
    pub merchant_id: Option<String>,
    pub google_pay: Option<GoogleApiModelData>,
    pub paypal_sdk: Option<PaypalSdkData>,
    pub apple_pay: Option<ApplePayData>,
    pub apple_pay_combined: Option<ApplePayData>,
    pub endpoint_prefix: Option<String>,
    pub mcc: Option<String>,
    pub merchant_country_code: Option<String>,
    pub merchant_name: Option<String>,
    pub acquirer_bin: Option<String>,
    pub acquirer_merchant_id: Option<String>,
    pub acquirer_country_code: Option<String>,
    pub three_ds_requestor_name: Option<String>,
    pub three_ds_requestor_id: Option<String>,
    pub pull_mechanism_for_external_3ds_enabled: Option<bool>,
    pub klarna_region: Option<KlarnaEndpoint>,
    pub source_balance_account: Option<String>,
    pub brand_id: Option<String>,
    pub destination_account_number: Option<String>,
    pub dpa_id: Option<String>,
    pub dpa_name: Option<String>,
    pub locale: Option<String>,
    pub card_brands: Option<Vec<String>>,
    pub merchant_category_code: Option<String>,
    pub merchant_configuration_id: Option<String>,
    pub tenant_id: Option<String>,
    pub platform_url: Option<String>,
    pub account_id: Option<String>,
}

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

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, ToSchema, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CardProvider {
    pub payment_method_type: api_models::enums::CardNetwork,
    /// List of currencies accepted or has the processing capabilities of the processor
    #[schema(example = json!(
            {
                "type": "specific_accepted",
                "list": ["USD", "INR"]
            }
        ), value_type = Option<AcceptedCurrencies>)]
    pub accepted_currencies: Option<api_models::admin::AcceptedCurrencies>,
    #[schema(example = json!(
        {
            "type": "specific_accepted",
            "list": ["UK", "AU"]
        }
    ), value_type = Option<AcceptedCountries>)]
    pub accepted_countries: Option<api_models::admin::AcceptedCountries>,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, ToSchema, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Provider {
    pub payment_method_type: api_models::enums::PaymentMethodType,
    /// List of currencies accepted or has the processing capabilities of the processor
    #[schema(example = json!(
            {
                "type": "specific_accepted",
                "list": ["USD", "INR"]
            }
        ), value_type = Option<AcceptedCurrencies>)]
    pub accepted_currencies: Option<api_models::admin::AcceptedCurrencies>,
    #[schema(example = json!(
        {
            "type": "specific_accepted",
            "list": ["UK", "AU"]
        }
    ), value_type = Option<AcceptedCountries>)]
    pub accepted_countries: Option<api_models::admin::AcceptedCountries>,
    pub payment_experience: Option<api_models::enums::PaymentExperience>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub struct ConnectorApiIntegrationPayload {
    pub connector_type: String,
    pub profile_id: common_utils::id_type::ProfileId,
    pub connector_name: api_models::enums::Connector,
    #[serde(skip_deserializing)]
    #[schema(example = "stripe_US_travel")]
    pub connector_label: Option<String>,
    pub merchant_connector_id: Option<String>,
    pub disabled: bool,
    pub test_mode: bool,
    pub payment_methods_enabled: Option<Vec<PaymentMethodsEnabled>>,
    pub metadata: Option<ApiModelMetaData>,
    pub connector_webhook_details: Option<api_models::admin::MerchantConnectorWebhookDetails>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct DashboardPaymentMethodPayload {
    pub payment_method: api_models::enums::PaymentMethod,
    pub payment_method_type: String,
    pub provider: Option<Vec<Provider>>,
    pub card_provider: Option<Vec<CardProvider>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde_with::skip_serializing_none]
#[serde(rename_all = "snake_case")]
pub struct DashboardRequestPayload {
    pub connector: api_models::enums::Connector,
    pub payment_methods_enabled: Option<Vec<DashboardPaymentMethodPayload>>,
    pub metadata: Option<ApiModelMetaData>,
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, serde::Serialize, Clone)]
#[serde(tag = "type", content = "options")]
pub enum InputType {
    Text,
    Number,
    Toggle,
    Radio(Vec<String>),
    Select(Vec<String>),
    MultiSelect(Vec<String>),
}

#[serde_with::skip_serializing_none]
#[derive(Debug, Deserialize, serde::Serialize, Clone)]
#[serde(rename_all = "snake_case")]
pub struct InputData {
    pub name: String,
    pub label: String,
    pub placeholder: String,
    pub required: bool,
    #[serde(flatten)]
    pub input_type: InputType,
}


pub struct PixBankTransfer {
    /// Bank name
    #[schema(value_type = Option<String>, example = "Deutsche Bank")]
    pub bank_name: Option<String>,

    /// Bank branch
    #[schema(value_type = Option<String>, example = "3707")]
    pub bank_branch: Option<String>,

    /// Bank account number is an unique identifier assigned by a bank to a customer.
    #[schema(value_type = String, example = "000123456")]
    pub bank_account_number: Secret<String>,

    /// Unique key for pix customer
    #[schema(value_type = String, example = "000123456")]
    pub pix_key: Secret<String>,

    /// Individual taxpayer identification number
    #[schema(value_type = Option<String>, example = "000123456")]
    pub tax_id: Option<Secret<String>>,
}


pub fn set_cargo_workspace_members_env() {
    use std::io::Write;

    let metadata = cargo_metadata::MetadataCommand::new()
        .exec()
        .expect("Failed to obtain cargo metadata");

    let workspace_members = metadata
        .workspace_packages()
        .iter()
        .map(|package| package.name.as_str())
        .collect::<Vec<_>>()
        .join(",");

    writeln!(
        &mut std::io::stdout(),
        "cargo:rustc-env=CARGO_WORKSPACE_MEMBERS={workspace_members}"
    )
    .expect("Failed to set `CARGO_WORKSPACE_MEMBERS` environment variable");
}


pub struct SubmitEvidenceRequestData {
    pub dispute_id: String,
    pub dispute_status: storage_enums::DisputeStatus,
    pub connector_dispute_id: String,
    pub access_activity_log: Option<String>,
    pub billing_address: Option<String>,
    //cancellation policy
    pub cancellation_policy: Option<Vec<u8>>,
    pub cancellation_policy_file_type: Option<String>,
    pub cancellation_policy_provider_file_id: Option<String>,
    pub cancellation_policy_disclosure: Option<String>,
    pub cancellation_rebuttal: Option<String>,
    //customer communication
    pub customer_communication: Option<Vec<u8>>,
    pub customer_communication_file_type: Option<String>,
    pub customer_communication_provider_file_id: Option<String>,
    pub customer_email_address: Option<String>,
    pub customer_name: Option<String>,
    pub customer_purchase_ip: Option<String>,
    //customer signature
    pub customer_signature: Option<Vec<u8>>,
    pub customer_signature_file_type: Option<String>,
    pub customer_signature_provider_file_id: Option<String>,
    //product description
    pub product_description: Option<String>,
    //receipts
    pub receipt: Option<Vec<u8>>,
    pub receipt_file_type: Option<String>,
    pub receipt_provider_file_id: Option<String>,
    //refund policy
    pub refund_policy: Option<Vec<u8>>,
    pub refund_policy_file_type: Option<String>,
    pub refund_policy_provider_file_id: Option<String>,
    pub refund_policy_disclosure: Option<String>,
    pub refund_refusal_explanation: Option<String>,
    //service docs
    pub service_date: Option<String>,
    pub service_documentation: Option<Vec<u8>>,
    pub service_documentation_file_type: Option<String>,
    pub service_documentation_provider_file_id: Option<String>,
    //shipping details docs
    pub shipping_address: Option<String>,
    pub shipping_carrier: Option<String>,
    pub shipping_date: Option<String>,
    pub shipping_documentation: Option<Vec<u8>>,
    pub shipping_documentation_file_type: Option<String>,
    pub shipping_documentation_provider_file_id: Option<String>,
    pub shipping_tracking_number: Option<String>,
    //invoice details
    pub invoice_showing_distinct_transactions: Option<Vec<u8>>,
    pub invoice_showing_distinct_transactions_file_type: Option<String>,
    pub invoice_showing_distinct_transactions_provider_file_id: Option<String>,
    //subscription details
    pub recurring_transaction_agreement: Option<Vec<u8>>,
    pub recurring_transaction_agreement_file_type: Option<String>,
    pub recurring_transaction_agreement_provider_file_id: Option<String>,
    //uncategorized details
    pub uncategorized_file: Option<Vec<u8>>,
    pub uncategorized_file_type: Option<String>,
    pub uncategorized_file_provider_file_id: Option<String>,
    pub uncategorized_text: Option<String>,
}


pub async fn refunds_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<refunds::RefundUpdateRequest>,
    path: web::Path<String>,
) -> HttpResponse {
    let flow = Flow::RefundsUpdate;
    let mut refund_update_req = json_payload.into_inner();
    refund_update_req.refund_id = path.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        refund_update_req,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            refund_update_core(state, merchant_context, req)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl RefundExecute for Threedsecureio {}

impl RefundExecute for for Threedsecureio