File: crates/hyperswitch_domain_models/src/relay.rs
 Public functions: 2
 Public structs: 2

use common_enums::enums;
use common_utils::{
    self,
    errors::{CustomResult, ValidationError},
    id_type::{self, GenerateId},
    pii,
    types::{keymanager, MinorUnit},
};
use diesel_models::relay::RelayUpdateInternal;
use error_stack::ResultExt;
use masking::{ExposeInterface, Secret};
use serde::{self, Deserialize, Serialize};
use time::PrimitiveDateTime;

use crate::{router_data::ErrorResponse, router_response_types};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Relay {
    pub id: id_type::RelayId,
    pub connector_resource_id: String,
    pub connector_id: id_type::MerchantConnectorAccountId,
    pub profile_id: id_type::ProfileId,
    pub merchant_id: id_type::MerchantId,
    pub relay_type: enums::RelayType,
    pub request_data: Option<RelayData>,
    pub status: enums::RelayStatus,
    pub connector_reference_id: Option<String>,
    pub error_code: Option<String>,
    pub error_message: Option<String>,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    pub modified_at: PrimitiveDateTime,
    pub response_data: Option<pii::SecretSerdeValue>,
}

impl Relay {
    pub fn new(
        relay_request: &api_models::relay::RelayRequest,
        merchant_id: &id_type::MerchantId,
        profile_id: &id_type::ProfileId,
    ) -> Self {
        let relay_id = id_type::RelayId::generate();
        Self {
            id: relay_id.clone(),
            connector_resource_id: relay_request.connector_resource_id.clone(),
            connector_id: relay_request.connector_id.clone(),
            profile_id: profile_id.clone(),
            merchant_id: merchant_id.clone(),
            relay_type: common_enums::RelayType::Refund,
            request_data: relay_request.data.clone().map(From::from),
            status: common_enums::RelayStatus::Created,
            connector_reference_id: None,
            error_code: None,
            error_message: None,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            response_data: None,
        }
    }
}

impl From<api_models::relay::RelayData> for RelayData {
    fn from(relay: api_models::relay::RelayData) -> Self {
        match relay {
            api_models::relay::RelayData::Refund(relay_refund_request) => {
                Self::Refund(RelayRefundData {
                    amount: relay_refund_request.amount,
                    currency: relay_refund_request.currency,
                    reason: relay_refund_request.reason,
                })
            }
        }
    }
}

impl From<api_models::relay::RelayRefundRequestData> for RelayRefundData {
    fn from(relay: api_models::relay::RelayRefundRequestData) -> Self {
        Self {
            amount: relay.amount,
            currency: relay.currency,
            reason: relay.reason,
        }
    }
}

impl RelayUpdate {
    pub fn from(
        response: Result<router_response_types::RefundsResponseData, ErrorResponse>,
    ) -> Self {
        match response {
            Err(error) => Self::ErrorUpdate {
                error_code: error.code,
                error_message: error.reason.unwrap_or(error.message),
                status: common_enums::RelayStatus::Failure,
            },
            Ok(response) => Self::StatusUpdate {
                connector_reference_id: Some(response.connector_refund_id),
                status: common_enums::RelayStatus::from(response.refund_status),
            },
        }
    }
}

impl From<RelayData> for api_models::relay::RelayData {
    fn from(relay: RelayData) -> Self {
        match relay {
            RelayData::Refund(relay_refund_request) => {
                Self::Refund(api_models::relay::RelayRefundRequestData {
                    amount: relay_refund_request.amount,
                    currency: relay_refund_request.currency,
                    reason: relay_refund_request.reason,
                })
            }
        }
    }
}

impl From<Relay> for api_models::relay::RelayResponse {
    fn from(value: Relay) -> Self {
        let error = value
            .error_code
            .zip(value.error_message)
            .map(
                |(error_code, error_message)| api_models::relay::RelayError {
                    code: error_code,
                    message: error_message,
                },
            );

        let data = value.request_data.map(|relay_data| match relay_data {
            RelayData::Refund(relay_refund_request) => {
                api_models::relay::RelayData::Refund(api_models::relay::RelayRefundRequestData {
                    amount: relay_refund_request.amount,
                    currency: relay_refund_request.currency,
                    reason: relay_refund_request.reason,
                })
            }
        });
        Self {
            id: value.id,
            status: value.status,
            error,
            connector_resource_id: value.connector_resource_id,
            connector_id: value.connector_id,
            profile_id: value.profile_id,
            relay_type: value.relay_type,
            data,
            connector_reference_id: value.connector_reference_id,
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case", untagged)]
pub enum RelayData {
    Refund(RelayRefundData),
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RelayRefundData {
    pub amount: MinorUnit,
    pub currency: enums::Currency,
    pub reason: Option<String>,
}

#[derive(Debug)]
pub enum RelayUpdate {
    ErrorUpdate {
        error_code: String,
        error_message: String,
        status: enums::RelayStatus,
    },
    StatusUpdate {
        connector_reference_id: Option<String>,
        status: common_enums::RelayStatus,
    },
}

impl From<RelayUpdate> for RelayUpdateInternal {
    fn from(value: RelayUpdate) -> Self {
        match value {
            RelayUpdate::ErrorUpdate {
                error_code,
                error_message,
                status,
            } => Self {
                error_code: Some(error_code),
                error_message: Some(error_message),
                connector_reference_id: None,
                status: Some(status),
                modified_at: common_utils::date_time::now(),
            },
            RelayUpdate::StatusUpdate {
                connector_reference_id,
                status,
            } => Self {
                connector_reference_id,
                status: Some(status),
                error_code: None,
                error_message: None,
                modified_at: common_utils::date_time::now(),
            },
        }
    }
}

#[async_trait::async_trait]
impl super::behaviour::Conversion for Relay {
    type DstType = diesel_models::relay::Relay;
    type NewDstType = diesel_models::relay::RelayNew;

    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(diesel_models::relay::Relay {
            id: self.id,
            connector_resource_id: self.connector_resource_id,
            connector_id: self.connector_id,
            profile_id: self.profile_id,
            merchant_id: self.merchant_id,
            relay_type: self.relay_type,
            request_data: self
                .request_data
                .map(|data| {
                    serde_json::to_value(data).change_context(ValidationError::InvalidValue {
                        message: "Failed while decrypting business profile data".to_string(),
                    })
                })
                .transpose()?
                .map(Secret::new),
            status: self.status,
            connector_reference_id: self.connector_reference_id,
            error_code: self.error_code,
            error_message: self.error_message,
            created_at: self.created_at,
            modified_at: self.modified_at,
            response_data: self.response_data,
        })
    }

    async fn convert_back(
        _state: &keymanager::KeyManagerState,
        item: Self::DstType,
        _key: &Secret<Vec<u8>>,
        _key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError> {
        Ok(Self {
            id: item.id,
            connector_resource_id: item.connector_resource_id,
            connector_id: item.connector_id,
            profile_id: item.profile_id,
            merchant_id: item.merchant_id,
            relay_type: enums::RelayType::Refund,
            request_data: item
                .request_data
                .map(|data| {
                    serde_json::from_value(data.expose()).change_context(
                        ValidationError::InvalidValue {
                            message: "Failed while decrypting business profile data".to_string(),
                        },
                    )
                })
                .transpose()?,
            status: item.status,
            connector_reference_id: item.connector_reference_id,
            error_code: item.error_code,
            error_message: item.error_message,
            created_at: item.created_at,
            modified_at: item.modified_at,
            response_data: item.response_data,
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        Ok(diesel_models::relay::RelayNew {
            id: self.id,
            connector_resource_id: self.connector_resource_id,
            connector_id: self.connector_id,
            profile_id: self.profile_id,
            merchant_id: self.merchant_id,
            relay_type: self.relay_type,
            request_data: self
                .request_data
                .map(|data| {
                    serde_json::to_value(data).change_context(ValidationError::InvalidValue {
                        message: "Failed while decrypting business profile data".to_string(),
                    })
                })
                .transpose()?
                .map(Secret::new),
            status: self.status,
            connector_reference_id: self.connector_reference_id,
            error_code: self.error_code,
            error_message: self.error_message,
            created_at: self.created_at,
            modified_at: self.modified_at,
            response_data: self.response_data,
        })
    }
}


File: crates/router/src/core/proxy.rs
 Public functions: 1

use super::errors::{self, RouterResponse, RouterResult};
use crate::{logger, routes::SessionState, services, types::domain};
pub mod utils;
use api_models::proxy as proxy_api_models;
use common_utils::{
    ext_traits::BytesExt,
    request::{self, RequestBuilder},
};
use error_stack::ResultExt;
use hyperswitch_interfaces::types::Response;
use serde_json::Value;

pub async fn proxy_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: proxy_api_models::ProxyRequest,
) -> RouterResponse<proxy_api_models::ProxyResponse> {
    let req_wrapper = utils::ProxyRequestWrapper(req.clone());
    let proxy_record = req_wrapper
        .get_proxy_record(
            &state,
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;

    let vault_data = proxy_record
        .get_vault_data(&state, merchant_context)
        .await?;

    let processed_body =
        interpolate_token_references_with_vault_data(req.request_body.clone(), &vault_data)?;

    let res = execute_proxy_request(&state, &req_wrapper, processed_body).await?;

    let proxy_response = proxy_api_models::ProxyResponse::try_from(ProxyResponseWrapper(res))?;

    Ok(services::ApplicationResponse::Json(proxy_response))
}

fn interpolate_token_references_with_vault_data(
    value: Value,
    vault_data: &Value,
) -> RouterResult<Value> {
    match value {
        Value::Object(obj) => {
            let new_obj = obj
                .into_iter()
                .map(|(key, val)| interpolate_token_references_with_vault_data(val, vault_data).map(|processed| (key, processed)))
                .collect::<Result<serde_json::Map<_, _>, error_stack::Report<errors::ApiErrorResponse>>>()?;

            Ok(Value::Object(new_obj))
        }
        Value::String(s) => utils::parse_token(&s)
            .map(|(_, token_ref)| extract_field_from_vault_data(vault_data, &token_ref.field))
            .unwrap_or(Ok(Value::String(s.clone()))),
        _ => Ok(value),
    }
}

fn find_field_recursively_in_vault_data(
    obj: &serde_json::Map<String, Value>,
    field_name: &str,
) -> Option<Value> {
    obj.get(field_name).cloned().or_else(|| {
        obj.values()
            .filter_map(|val| {
                if let Value::Object(inner_obj) = val {
                    find_field_recursively_in_vault_data(inner_obj, field_name)
                } else {
                    None
                }
            })
            .next()
    })
}

fn extract_field_from_vault_data(vault_data: &Value, field_name: &str) -> RouterResult<Value> {
    match vault_data {
        Value::Object(obj) => find_field_recursively_in_vault_data(obj, field_name)
            .ok_or_else(|| errors::ApiErrorResponse::InternalServerError)
            .attach_printable(format!("Field '{field_name}' not found")),
        _ => Err(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Vault data is not a valid JSON object"),
    }
}

async fn execute_proxy_request(
    state: &SessionState,
    req_wrapper: &utils::ProxyRequestWrapper,
    processed_body: Value,
) -> RouterResult<Response> {
    let request = RequestBuilder::new()
        .method(req_wrapper.get_method())
        .attach_default_headers()
        .headers(req_wrapper.get_headers())
        .url(req_wrapper.get_destination_url())
        .set_body(request::RequestContent::Json(Box::new(processed_body)))
        .build();

    let response = services::call_connector_api(state, request, "proxy")
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to call the destination");

    response
        .map(|inner| match inner {
            Err(err_res) => {
                logger::error!("Error while receiving response: {err_res:?}");
                err_res
            }
            Ok(res) => res,
        })
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error while receiving response")
}

struct ProxyResponseWrapper(Response);

impl TryFrom<ProxyResponseWrapper> for proxy_api_models::ProxyResponse {
    type Error = error_stack::Report<errors::ApiErrorResponse>;

    fn try_from(wrapper: ProxyResponseWrapper) -> Result<Self, Self::Error> {
        let res = wrapper.0;
        let response_body: Value = res
            .response
            .parse_struct("ProxyResponse")
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to parse the response")?;

        let status_code = res.status_code;
        let response_headers = proxy_api_models::Headers::from_header_map(res.headers.as_ref());

        Ok(Self {
            response: response_body,
            status_code,
            response_headers,
        })
    }
}


pub trait ToArchipelBillingAddress {
    fn to_archipel_billing_address(&self) -> Option<ArchipelBillingAddress>;
}


pub struct PayoutCreatePayoutLinkConfig {
    /// The unique identifier for the collect link.
    #[schema(value_type = Option<String>, example = "pm_collect_link_2bdacf398vwzq5n422S1")]
    pub payout_link_id: Option<String>,

    #[serde(flatten)]
    #[schema(value_type = Option<GenericLinkUiConfig>)]
    pub ui_config: Option<link_utils::GenericLinkUiConfig>,

    /// List of payout methods shown on collect UI
    #[schema(value_type = Option<Vec<EnabledPaymentMethod>>, example = r#"[{"payment_method": "bank_transfer", "payment_method_types": ["ach", "bacs"]}]"#)]
    pub enabled_payment_methods: Option<Vec<link_utils::EnabledPaymentMethod>>,

    /// Form layout of the payout link
    #[schema(value_type = Option<UIWidgetFormLayout>, max_length = 255, example = "tabs")]
    pub form_layout: Option<api_enums::UIWidgetFormLayout>,

    /// `test_mode` allows for opening payout links without any restrictions. This removes
    /// - domain name validations
    /// - check for making sure link is accessed within an iframe
    #[schema(value_type = Option<bool>, example = false)]
    pub test_mode: Option<bool>,
}


pub fn validate_adyen_charge_refund(
    adyen_split_payment_response: &common_types::domain::AdyenSplitData,
    adyen_split_refund_request: &common_types::domain::AdyenSplitData,
) -> RouterResult<()> {
    if adyen_split_refund_request.store != adyen_split_payment_response.store {
        return Err(report!(errors::ApiErrorResponse::InvalidDataValue {
            field_name: "split_payments.adyen_split_payment.store",
        }));
    };

    for refund_split_item in adyen_split_refund_request.split_items.iter() {
        let refund_split_reference = refund_split_item.reference.clone();
        let matching_payment_split_item = adyen_split_payment_response
            .split_items
            .iter()
            .find(|payment_split_item| refund_split_reference == payment_split_item.reference);

        if let Some(payment_split_item) = matching_payment_split_item {
            if let Some((refund_amount, payment_amount)) =
                refund_split_item.amount.zip(payment_split_item.amount)
            {
                if refund_amount > payment_amount {
                    return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                        message: format!(
                            "Invalid refund amount for split item, reference: {refund_split_reference}",

                        ),
                    }));
                }
            }

            if let Some((refund_account, payment_account)) = refund_split_item
                .account
                .as_ref()
                .zip(payment_split_item.account.as_ref())
            {
                if !refund_account.eq(payment_account) {
                    return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                        message: format!(
                            "Invalid refund account for split item, reference: {refund_split_reference}",

                        ),
                    }));
                }
            }

            if refund_split_item.split_type != payment_split_item.split_type {
                return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                    message: format!(
                        "Invalid refund split_type for split item, reference: {refund_split_reference}",

                    ),
                }));
            }
        } else {
            return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                message: format!(
                    "No matching payment split item found for reference: {refund_split_reference}",
                ),
            }));
        }
    }
    Ok(())
}


pub fn enum_nums(ts: TokenStream) -> TokenStream {
    inner::enum_nums_inner(ts)
}


pub struct FacilitapayPaymentsRequest {
    pub transaction: FacilitapayTransactionRequest,
}


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

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

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

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

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

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

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

        match (
            response.status,
            response.code,
            response.error,
            response.status_code,
            response.detail,
        ) {
            (Some(status), Some(code), _, _, _) => Ok(ErrorResponse {
                status_code: res.status_code,
                code: code.to_string(),
                message: status,
                reason: None,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
            (None, None, Some(nomupay_inner_error), _, _) => {
                match (
                    nomupay_inner_error.error_description,
                    nomupay_inner_error.validation_errors,
                ) {
                    (Some(error_description), _) => Ok(ErrorResponse {
                        status_code: res.status_code,
                        code: nomupay_inner_error.error_code,
                        message: error_description,
                        reason: None,
                        attempt_status: None,
                        connector_transaction_id: None,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    }),
                    (_, Some(validation_errors)) => Ok(ErrorResponse {
                        status_code: res.status_code,
                        code: nomupay_inner_error.error_code,
                        message: validation_errors
                            .first()
                            .map(|m| m.message.clone())
                            .unwrap_or_default(),
                        reason: Some(
                            validation_errors
                                .first()
                                .map(|m| m.field.clone())
                                .unwrap_or_default(),
                        ),
                        attempt_status: None,
                        connector_transaction_id: None,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    }),
                    (None, None) => Ok(ErrorResponse {
                        status_code: res.status_code,
                        code: NO_ERROR_CODE.to_string(),
                        message: NO_ERROR_MESSAGE.to_string(),
                        reason: None,
                        attempt_status: None,
                        connector_transaction_id: None,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    }),
                }
            }
            (None, None, None, Some(status_code), Some(detail)) => Ok(ErrorResponse {
                status_code,
                code: detail
                    .get(1)
                    .map(|d| d.error_type.clone())
                    .unwrap_or_default(),
                message: status_code.to_string(),
                reason: None,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
            _ => Ok(ErrorResponse {
                status_code: res.status_code,
                code: NO_ERROR_CODE.to_string(),
                message: NO_ERROR_MESSAGE.to_string(),
                reason: None,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
        }
    }
}


impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize {
    type PaymentFlowResponse = router_types::AuthenticatePaymentFlowResponse;

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

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

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

impl PaymentRedirectFlow for for PaymentAuthenticateCompleteAuthorize


pub struct JWTAuthMerchantFromRoute {
    pub merchant_id: id_type::MerchantId,
    pub required_permission: Permission,
}


pub struct ZenErrorResponse {
    pub error: Option<ZenErrorBody>,
    pub message: Option<String>,
}


    BankNowyBfgSa,
    GetinBank,
    Blik,
    NoblePay,
    #[serde(rename = "ideabank")]
    IdeaBank,
    #[serde(rename = "envelobank")]
    EnveloBank,
    NestPrzelew,
    MbankMtransfer,
    Inteligo,
    PbacZIpko,
    BnpParibas,
    BankPekaoSa,
    VolkswagenBank,
    AliorBank,
    Boz,
}

// This is used only for Disputes
impl From<WebhookEventStatus> for api_models::webhooks::IncomingWebhookEvent {
    fn from(value: WebhookEventStatus) -> Self {
        match value {
            WebhookEventStatus::WarningNeedsResponse => Self::DisputeOpened,
            WebhookEventStatus::WarningClosed => Self::DisputeCancelled,
            WebhookEventStatus::WarningUnderReview => Self::DisputeChallenged,
            WebhookEventStatus::Won => Self::DisputeWon,
            WebhookEventStatus::Lost => Self::DisputeLost,
            WebhookEventStatus::NeedsResponse
            | WebhookEventStatus::UnderReview
            | WebhookEventStatus::ChargeRefunded
            | WebhookEventStatus::Succeeded
            | WebhookEventStatus::RequiresPaymentMethod
            | WebhookEventStatus::RequiresConfirmation
            | WebhookEventStatus::RequiresAction
            | WebhookEventStatus::Processing
            | WebhookEventStatus::RequiresCapture
            | WebhookEventStatus::Canceled
            | WebhookEventStatus::Chargeable
            | WebhookEventStatus::Failed
            | WebhookEventStatus::Unknown => Self::EventNotSupported,
        }
    }
}

impl TryFrom<&enums::BankNames> for StripeBankNames {
    type Error = ConnectorError;
    fn try_from(bank: &enums::BankNames) -> Result<Self, Self::Error> {
        Ok(match bank {
            enums::BankNames::AbnAmro => Self::AbnAmro,
            enums::BankNames::ArzteUndApothekerBank => Self::ArzteUndApothekerBank,
            enums::BankNames::AsnBank => Self::AsnBank,
            enums::BankNames::AustrianAnadiBankAg => Self::AustrianAnadiBankAg,
            enums::BankNames::BankAustria => Self::BankAustria,
            enums::BankNames::BankhausCarlSpangler => Self::BankhausCarlSpangler,
            enums::BankNames::BankhausSchelhammerUndSchatteraAg => {
                Self::BankhausSchelhammerUndSchatteraAg
            }
            enums::BankNames::BawagPskAg => Self::BawagPskAg,
            enums::BankNames::BksBankAg => Self::BksBankAg,
            enums::BankNames::BrullKallmusBankAg => Self::BrullKallmusBankAg,
            enums::BankNames::BtvVierLanderBank => Self::BtvVierLanderBank,
            enums::BankNames::Bunq => Self::Bunq,
            enums::BankNames::CapitalBankGraweGruppeAg => Self::CapitalBankGraweGruppeAg,
            enums::BankNames::Citi => Self::CitiHandlowy,
            enums::BankNames::Dolomitenbank => Self::Dolomitenbank,
            enums::BankNames::EasybankAg => Self::EasybankAg,
            enums::BankNames::ErsteBankUndSparkassen => Self::ErsteBankUndSparkassen,
            enums::BankNames::Handelsbanken => Self::Handelsbanken,
            enums::BankNames::HypoAlpeadriabankInternationalAg => {
                Self::HypoAlpeadriabankInternationalAg
            }

            enums::BankNames::HypoNoeLbFurNiederosterreichUWien => {
                Self::HypoNoeLbFurNiederosterreichUWien
            }
            enums::BankNames::HypoOberosterreichSalzburgSteiermark => {
                Self::HypoOberosterreichSalzburgSteiermark
            }
            enums::BankNames::HypoTirolBankAg => Self::HypoTirolBankAg,
            enums::BankNames::HypoVorarlbergBankAg => Self::HypoVorarlbergBankAg,
            enums::BankNames::HypoBankBurgenlandAktiengesellschaft => {
                Self::HypoBankBurgenlandAktiengesellschaft
            }
            enums::BankNames::Ing => Self::Ing,
            enums::BankNames::Knab => Self::Knab,
            enums::BankNames::MarchfelderBank => Self::MarchfelderBank,
            enums::BankNames::OberbankAg => Self::OberbankAg,
            enums::BankNames::RaiffeisenBankengruppeOsterreich => {
                Self::RaiffeisenBankengruppeOsterreich
            }
            enums::BankNames::Rabobank => Self::Rabobank,
            enums::BankNames::Regiobank => Self::Regiobank,
            enums::BankNames::Revolut => Self::Revolut,
            enums::BankNames::SnsBank => Self::SnsBank,
            enums::BankNames::TriodosBank => Self::TriodosBank,
            enums::BankNames::VanLanschot => Self::VanLanschot,
            enums::BankNames::Moneyou => Self::Moneyou,
            enums::BankNames::SchoellerbankAg => Self::SchoellerbankAg,
            enums::BankNames::SpardaBankWien => Self::SpardaBankWien,
            enums::BankNames::VolksbankGruppe => Self::VolksbankGruppe,
            enums::BankNames::VolkskreditbankAg => Self::VolkskreditbankAg,
            enums::BankNames::VrBankBraunau => Self::VrBankBraunau,
            enums::BankNames::PlusBank => Self::PlusBank,
            enums::BankNames::EtransferPocztowy24 => Self::EtransferPocztowy24,
            enums::BankNames::BankiSpbdzielcze => Self::BankiSpbdzielcze,
            enums::BankNames::BankNowyBfgSa => Self::BankNowyBfgSa,
            enums::BankNames::GetinBank => Self::GetinBank,
            enums::BankNames::Blik => Self::Blik,
            enums::BankNames::NoblePay => Self::NoblePay,
            enums::BankNames::IdeaBank => Self::IdeaBank,
            enums::BankNames::EnveloBank => Self::EnveloBank,
            enums::BankNames::NestPrzelew => Self::NestPrzelew,
            enums::BankNames::MbankMtransfer => Self::MbankMtransfer,
            enums::BankNames::Inteligo => Self::Inteligo,
            enums::BankNames::PbacZIpko => Self::PbacZIpko,
            enums::BankNames::BnpParibas => Self::BnpParibas,
            enums::BankNames::BankPekaoSa => Self::BankPekaoSa,
            enums::BankNames::VolkswagenBank => Self::VolkswagenBank,
            enums::BankNames::AliorBank => Self::AliorBank,
            enums::BankNames::Boz => Self::Boz,

            _ => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            ))?,
        })
    }
}

fn validate_shipping_address_against_payment_method(
    shipping_address: &Option<StripeShippingAddress>,
    payment_method: Option<&StripePaymentMethodType>,
) -> Result<(), error_stack::Report<ConnectorError>> {
    match payment_method {
        Some(StripePaymentMethodType::AfterpayClearpay) => match shipping_address {
            Some(address) => {
                let missing_fields = collect_missing_value_keys!(
                    ("shipping.address.line1", address.line1),
                    ("shipping.address.country", address.country),
                    ("shipping.address.zip", address.zip)
                );

                if !missing_fields.is_empty() {
                    return Err(ConnectorError::MissingRequiredFields {
                        field_names: missing_fields,
                    }
                    .into());
                }
                Ok(())
            }
            None => Err(ConnectorError::MissingRequiredField {
                field_name: "shipping.address",
            }
            .into()),
        },
        _ => Ok(()),
    }
}

impl TryFrom<&PayLaterData> for StripePaymentMethodType {
    type Error = ConnectorError;
    fn try_from(pay_later_data: &PayLaterData) -> Result<Self, Self::Error> {
        match pay_later_data {
            PayLaterData::KlarnaRedirect { .. } => Ok(Self::Klarna),
            PayLaterData::AffirmRedirect {} => Ok(Self::Affirm),
            PayLaterData::AfterpayClearpayRedirect { .. } => Ok(Self::AfterpayClearpay),

            PayLaterData::KlarnaSdk { .. }
            | PayLaterData::PayBrightRedirect {}
            | PayLaterData::WalleyRedirect {}
            | PayLaterData::AlmaRedirect {}
            | PayLaterData::FlexitiRedirect { .. }
            | PayLaterData::AtomeRedirect {}
            | PayLaterData::BreadpayRedirect {} => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )),
        }
    }
}

impl TryFrom<&BankRedirectData> for StripePaymentMethodType {
    type Error = ConnectorError;
    fn try_from(bank_redirect_data: &BankRedirectData) -> Result<Self, Self::Error> {
        match bank_redirect_data {
            BankRedirectData::Giropay { .. } => Ok(Self::Giropay),
            BankRedirectData::Ideal { .. } => Ok(Self::Ideal),
            BankRedirectData::Sofort { .. } => Ok(Self::Sofort),
            BankRedirectData::BancontactCard { .. } => Ok(Self::Bancontact),
            BankRedirectData::Przelewy24 { .. } => Ok(Self::Przelewy24),
            BankRedirectData::Eps { .. } => Ok(Self::Eps),
            BankRedirectData::Blik { .. } => Ok(Self::Blik),
            BankRedirectData::OnlineBankingFpx { .. } => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )),
            BankRedirectData::Bizum {}
            | BankRedirectData::Interac { .. }
            | BankRedirectData::Eft { .. }
            | BankRedirectData::OnlineBankingCzechRepublic { .. }
            | BankRedirectData::OnlineBankingFinland { .. }
            | BankRedirectData::OnlineBankingPoland { .. }
            | BankRedirectData::OnlineBankingSlovakia { .. }
            | BankRedirectData::OnlineBankingThailand { .. }
            | BankRedirectData::OpenBankingUk { .. }
            | BankRedirectData::Trustly { .. }
            | BankRedirectData::LocalBankRedirect {} => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )),
        }
    }
}

fn get_stripe_payment_method_type_from_wallet_data(
    wallet_data: &WalletData,
) -> Result<Option<StripePaymentMethodType>, ConnectorError> {
    match wallet_data {
        WalletData::AliPayRedirect(_) => Ok(Some(StripePaymentMethodType::Alipay)),
        WalletData::ApplePay(_) => Ok(None),
        WalletData::GooglePay(_) => Ok(Some(StripePaymentMethodType::Card)),
        WalletData::WeChatPayQr(_) => Ok(Some(StripePaymentMethodType::Wechatpay)),
        WalletData::CashappQr(_) => Ok(Some(StripePaymentMethodType::Cashapp)),
        WalletData::AmazonPayRedirect(_) => Ok(Some(StripePaymentMethodType::AmazonPay)),
        WalletData::RevolutPay(_) => Ok(Some(StripePaymentMethodType::RevolutPay)),
        WalletData::MobilePayRedirect(_) => Err(ConnectorError::NotImplemented(
            get_unimplemented_payment_method_error_message("stripe"),
        )),
        WalletData::PaypalRedirect(_)
        | WalletData::AliPayQr(_)
        | WalletData::BluecodeRedirect {}
        | WalletData::Paysera(_)
        | WalletData::Skrill(_)
        | WalletData::AmazonPay(_)
        | WalletData::AliPayHkRedirect(_)
        | WalletData::MomoRedirect(_)
        | WalletData::KakaoPayRedirect(_)
        | WalletData::GoPayRedirect(_)
        | WalletData::GcashRedirect(_)
        | WalletData::ApplePayRedirect(_)
        | WalletData::ApplePayThirdPartySdk(_)
        | WalletData::DanaRedirect {}
        | WalletData::GooglePayRedirect(_)
        | WalletData::GooglePayThirdPartySdk(_)
        | WalletData::MbWayRedirect(_)
        | WalletData::PaypalSdk(_)
        | WalletData::Paze(_)
        | WalletData::SamsungPay(_)
        | WalletData::TwintRedirect {}
        | WalletData::VippsRedirect {}
        | WalletData::TouchNGoRedirect(_)
        | WalletData::SwishQr(_)
        | WalletData::WeChatPayRedirect(_)
        | WalletData::Mifinity(_) => Err(ConnectorError::NotImplemented(
            get_unimplemented_payment_method_error_message("stripe"),
        )),
    }
}

impl From<&payment_method_data::BankDebitData> for StripePaymentMethodType {
    fn from(bank_debit_data: &payment_method_data::BankDebitData) -> Self {
        match bank_debit_data {
            payment_method_data::BankDebitData::AchBankDebit { .. } => Self::Ach,
            payment_method_data::BankDebitData::SepaBankDebit { .. } => Self::Sepa,
            payment_method_data::BankDebitData::BecsBankDebit { .. } => Self::Becs,
            payment_method_data::BankDebitData::BacsBankDebit { .. } => Self::Bacs,
        }
    }
}

fn get_bank_debit_data(
    bank_debit_data: &payment_method_data::BankDebitData,
) -> (StripePaymentMethodType, BankDebitData) {
    match bank_debit_data {
        payment_method_data::BankDebitData::AchBankDebit {
            account_number,
            routing_number,
            ..
        } => {
            let ach_data = BankDebitData::Ach {
                account_holder_type: "individual".to_string(),
                account_number: account_number.to_owned(),
                routing_number: routing_number.to_owned(),
            };
            (StripePaymentMethodType::Ach, ach_data)
        }
        payment_method_data::BankDebitData::SepaBankDebit { iban, .. } => {
            let sepa_data: BankDebitData = BankDebitData::Sepa {
                iban: iban.to_owned(),
            };
            (StripePaymentMethodType::Sepa, sepa_data)
        }
        payment_method_data::BankDebitData::BecsBankDebit {
            account_number,
            bsb_number,
            ..
        } => {
            let becs_data = BankDebitData::Becs {
                account_number: account_number.to_owned(),
                bsb_number: bsb_number.to_owned(),
            };
            (StripePaymentMethodType::Becs, becs_data)
        }
        payment_method_data::BankDebitData::BacsBankDebit {
            account_number,
            sort_code,
            ..
        } => {
            let bacs_data = BankDebitData::Bacs {
                account_number: account_number.to_owned(),
                sort_code: Secret::new(sort_code.clone().expose().replace('-', "")),
            };
            (StripePaymentMethodType::Bacs, bacs_data)
        }
    }
}

fn create_stripe_payment_method(
    payment_method_data: &PaymentMethodData,
    auth_type: enums::AuthenticationType,
    payment_method_token: Option<PaymentMethodToken>,
    is_customer_initiated_mandate_payment: Option<bool>,
    billing_address: StripeBillingAddress,
    request_incremental_authorization: bool,
    request_extended_authorization: Option<primitive_wrappers::RequestExtendedAuthorizationBool>,
) -> Result<
    (
        StripePaymentMethodData,
        Option<StripePaymentMethodType>,
        StripeBillingAddress,
    ),
    error_stack::Report<ConnectorError>,
> {
    match payment_method_data {
        PaymentMethodData::Card(card_details) => {
            let payment_method_auth_type = match auth_type {
                enums::AuthenticationType::ThreeDs => Auth3ds::Any,
                enums::AuthenticationType::NoThreeDs => Auth3ds::Automatic,
            };
            Ok((
                StripePaymentMethodData::try_from((
                    card_details,
                    payment_method_auth_type,
                    request_incremental_authorization,
                    request_extended_authorization,
                ))?,
                Some(StripePaymentMethodType::Card),
                billing_address,
            ))
        }
        PaymentMethodData::PayLater(pay_later_data) => {
            let stripe_pm_type = StripePaymentMethodType::try_from(pay_later_data)?;

            Ok((
                StripePaymentMethodData::PayLater(StripePayLaterData {
                    payment_method_data_type: stripe_pm_type,
                }),
                Some(stripe_pm_type),
                billing_address,
            ))
        }
        PaymentMethodData::BankRedirect(bank_redirect_data) => {
            let billing_address = if is_customer_initiated_mandate_payment == Some(true) {
                mandatory_parameters_for_sepa_bank_debit_mandates(
                    &Some(billing_address.to_owned()),
                    is_customer_initiated_mandate_payment,
                )?
            } else {
                billing_address
            };
            let pm_type = StripePaymentMethodType::try_from(bank_redirect_data)?;
            let bank_redirect_data = StripePaymentMethodData::try_from(bank_redirect_data)?;

            Ok((bank_redirect_data, Some(pm_type), billing_address))
        }
        PaymentMethodData::Wallet(wallet_data) => {
            let pm_type = get_stripe_payment_method_type_from_wallet_data(wallet_data)?;
            let wallet_specific_data =
                StripePaymentMethodData::try_from((wallet_data, payment_method_token))?;
            Ok((
                wallet_specific_data,
                pm_type,
                StripeBillingAddress::default(),
            ))
        }
        PaymentMethodData::BankDebit(bank_debit_data) => {
            let (pm_type, bank_debit_data) = get_bank_debit_data(bank_debit_data);

            let pm_data = StripePaymentMethodData::BankDebit(StripeBankDebitData {
                bank_specific_data: bank_debit_data,
            });

            Ok((pm_data, Some(pm_type), billing_address))
        }
        PaymentMethodData::BankTransfer(bank_transfer_data) => match bank_transfer_data.deref() {
            payment_method_data::BankTransferData::AchBankTransfer {} => Ok((
                StripePaymentMethodData::BankTransfer(StripeBankTransferData::AchBankTransfer(
                    Box::new(AchTransferData {
                        payment_method_data_type: StripePaymentMethodType::CustomerBalance,
                        bank_transfer_type: StripeCreditTransferTypes::AchCreditTransfer,
                        payment_method_type: StripePaymentMethodType::CustomerBalance,
                        balance_funding_type: BankTransferType::BankTransfers,
                    }),
                )),
                None,
                StripeBillingAddress::default(),
            )),
            payment_method_data::BankTransferData::MultibancoBankTransfer {} => Ok((
                StripePaymentMethodData::BankTransfer(
                    StripeBankTransferData::MultibancoBankTransfers(Box::new(
                        MultibancoTransferData {
                            payment_method_data_type: StripeCreditTransferTypes::Multibanco,
                            payment_method_type: StripeCreditTransferTypes::Multibanco,
                            email: billing_address.email.ok_or(
                                ConnectorError::MissingRequiredField {
                                    field_name: "billing_address.email",
                                },
                            )?,
                        },
                    )),
                ),
                None,
                StripeBillingAddress::default(),
            )),
            payment_method_data::BankTransferData::SepaBankTransfer {} => Ok((
                StripePaymentMethodData::BankTransfer(StripeBankTransferData::SepaBankTransfer(
                    Box::new(SepaBankTransferData {
                        payment_method_data_type: StripePaymentMethodType::CustomerBalance,
                        bank_transfer_type: BankTransferType::EuBankTransfer,
                        balance_funding_type: BankTransferType::BankTransfers,
                        payment_method_type: StripePaymentMethodType::CustomerBalance,
                        country: billing_address.country.ok_or(
                            ConnectorError::MissingRequiredField {
                                field_name: "billing_address.country",
                            },
                        )?,
                    }),
                )),
                Some(StripePaymentMethodType::CustomerBalance),
                billing_address,
            )),
            payment_method_data::BankTransferData::BacsBankTransfer {} => Ok((
                StripePaymentMethodData::BankTransfer(StripeBankTransferData::BacsBankTransfers(
                    Box::new(BacsBankTransferData {
                        payment_method_data_type: StripePaymentMethodType::CustomerBalance,
                        bank_transfer_type: BankTransferType::GbBankTransfer,
                        balance_funding_type: BankTransferType::BankTransfers,
                        payment_method_type: StripePaymentMethodType::CustomerBalance,
                    }),
                )),
                Some(StripePaymentMethodType::CustomerBalance),
                billing_address,
            )),
            payment_method_data::BankTransferData::Pix { .. } => Err(
                ConnectorError::NotImplemented(get_unimplemented_payment_method_error_message(
                    "stripe",
                ))
                .into(),
            ),
            payment_method_data::BankTransferData::Pse {}
            | payment_method_data::BankTransferData::LocalBankTransfer { .. }
            | payment_method_data::BankTransferData::InstantBankTransfer {}
            | payment_method_data::BankTransferData::InstantBankTransferFinland { .. }
            | payment_method_data::BankTransferData::InstantBankTransferPoland { .. }
            | payment_method_data::BankTransferData::PermataBankTransfer { .. }
            | payment_method_data::BankTransferData::BcaBankTransfer { .. }
            | payment_method_data::BankTransferData::BniVaBankTransfer { .. }
            | payment_method_data::BankTransferData::BriVaBankTransfer { .. }
            | payment_method_data::BankTransferData::CimbVaBankTransfer { .. }
            | payment_method_data::BankTransferData::DanamonVaBankTransfer { .. }
            | payment_method_data::BankTransferData::IndonesianBankTransfer { .. }
            | payment_method_data::BankTransferData::MandiriVaBankTransfer { .. } => Err(
                ConnectorError::NotImplemented(get_unimplemented_payment_method_error_message(
                    "stripe",
                ))
                .into(),
            ),
        },
        PaymentMethodData::Crypto(_) => Err(ConnectorError::NotImplemented(
            get_unimplemented_payment_method_error_message("stripe"),
        )
        .into()),

        PaymentMethodData::GiftCard(giftcard_data) => match giftcard_data.deref() {
            GiftCardData::Givex(_)
            | GiftCardData::PaySafeCard {}
            | GiftCardData::BhnCardNetwork(_) => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        },
        PaymentMethodData::CardRedirect(cardredirect_data) => match cardredirect_data {
            CardRedirectData::Knet {}
            | CardRedirectData::Benefit {}
            | CardRedirectData::MomoAtm {}
            | CardRedirectData::CardRedirect {} => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        },
        PaymentMethodData::Reward => Err(ConnectorError::NotImplemented(
            get_unimplemented_payment_method_error_message("stripe"),
        )
        .into()),

        PaymentMethodData::Voucher(voucher_data) => match voucher_data {
            VoucherData::Boleto(_) | VoucherData::Oxxo => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
            VoucherData::Alfamart(_)
            | VoucherData::Efecty
            | VoucherData::PagoEfectivo
            | VoucherData::RedCompra
            | VoucherData::RedPagos
            | VoucherData::Indomaret(_)
            | VoucherData::SevenEleven(_)
            | VoucherData::Lawson(_)
            | VoucherData::MiniStop(_)
            | VoucherData::FamilyMart(_)
            | VoucherData::Seicomart(_)
            | VoucherData::PayEasy(_) => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        },

        PaymentMethodData::Upi(_)
        | PaymentMethodData::RealTimePayment(_)
        | PaymentMethodData::MobilePayment(_)
        | PaymentMethodData::MandatePayment
        | PaymentMethodData::OpenBanking(_)
        | PaymentMethodData::CardToken(_)
        | PaymentMethodData::NetworkToken(_)
        | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => Err(
            ConnectorError::NotImplemented(get_unimplemented_payment_method_error_message(
                "stripe",
            ))
            .into(),
        ),
    }
}

fn get_stripe_card_network(card_network: common_enums::CardNetwork) -> Option<StripeCardNetwork> {
    match card_network {
        common_enums::CardNetwork::Visa => Some(StripeCardNetwork::Visa),
        common_enums::CardNetwork::Mastercard => Some(StripeCardNetwork::Mastercard),
        common_enums::CardNetwork::CartesBancaires => Some(StripeCardNetwork::CartesBancaires),
        common_enums::CardNetwork::AmericanExpress
        | common_enums::CardNetwork::JCB
        | common_enums::CardNetwork::DinersClub
        | common_enums::CardNetwork::Discover
        | common_enums::CardNetwork::UnionPay
        | common_enums::CardNetwork::Interac
        | common_enums::CardNetwork::RuPay
        | common_enums::CardNetwork::Maestro
        | common_enums::CardNetwork::Star
        | common_enums::CardNetwork::Accel
        | common_enums::CardNetwork::Pulse
        | common_enums::CardNetwork::Nyce => None,
    }
}

impl
    TryFrom<(
        &Card,
        Auth3ds,
        bool,
        Option<primitive_wrappers::RequestExtendedAuthorizationBool>,
    )> for StripePaymentMethodData
{
    type Error = ConnectorError;
    fn try_from(
        (
            card,
            payment_method_auth_type,
            request_incremental_authorization,
            request_extended_authorization,
        ): (
            &Card,
            Auth3ds,
            bool,
            Option<primitive_wrappers::RequestExtendedAuthorizationBool>,
        ),
    ) -> Result<Self, Self::Error> {
        Ok(Self::Card(StripeCardData {
            payment_method_data_type: StripePaymentMethodType::Card,
            payment_method_data_card_number: card.card_number.clone(),
            payment_method_data_card_exp_month: card.card_exp_month.clone(),
            payment_method_data_card_exp_year: card.card_exp_year.clone(),
            payment_method_data_card_cvc: Some(card.card_cvc.clone()),
            payment_method_auth_type: Some(payment_method_auth_type),
            payment_method_data_card_preferred_network: card
                .card_network
                .clone()
                .and_then(get_stripe_card_network),
            request_incremental_authorization: if request_incremental_authorization {
                Some(StripeRequestIncrementalAuthorization::IfAvailable)
            } else {
                None
            },
            request_extended_authorization: if request_extended_authorization
                .map(|request_extended_authorization| request_extended_authorization.is_true())
                .unwrap_or(false)
            {
                Some(StripeRequestExtendedAuthorization::IfAvailable)
            } else {
                None
            },
        }))
    }
}

impl TryFrom<(&WalletData, Option<PaymentMethodToken>)> for StripePaymentMethodData {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        (wallet_data, payment_method_token): (&WalletData, Option<PaymentMethodToken>),
    ) -> Result<Self, Self::Error> {
        match wallet_data {
            WalletData::ApplePay(applepay_data) => {
                let mut apple_pay_decrypt_data =
                    if let Some(PaymentMethodToken::ApplePayDecrypt(decrypt_data)) =
                        payment_method_token
                    {
                        let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year();
                        Some(Self::Wallet(StripeWallet::ApplePayPredecryptToken(
                            Box::new(StripeApplePayPredecrypt {
                                number: decrypt_data.clone().application_primary_account_number,
                                exp_year: expiry_year_4_digit,
                                exp_month: decrypt_data.application_expiration_month,
                                eci: decrypt_data.payment_data.eci_indicator,
                                cryptogram: decrypt_data.payment_data.online_payment_cryptogram,
                                tokenization_method: "apple_pay".to_string(),
                            }),
                        )))
                    } else {
                        None
                    };

                if apple_pay_decrypt_data.is_none() {
                    apple_pay_decrypt_data =
                        Some(Self::Wallet(StripeWallet::ApplepayToken(StripeApplePay {
                            pk_token: applepay_data.get_applepay_decoded_payment_data()?,
                            pk_token_instrument_name: applepay_data
                                .payment_method
                                .pm_type
                                .to_owned(),
                            pk_token_payment_network: applepay_data
                                .payment_method
                                .network
                                .to_owned(),
                            pk_token_transaction_id: Secret::new(
                                applepay_data.transaction_identifier.to_owned(),
                            ),
                        })));
                };
                let pmd = apple_pay_decrypt_data.ok_or(ConnectorError::MissingApplePayTokenData)?;
                Ok(pmd)
            }
            WalletData::WeChatPayQr(_) => Ok(Self::Wallet(StripeWallet::WechatpayPayment(
                WechatpayPayment {
                    client: WechatClient::Web,
                    payment_method_data_type: StripePaymentMethodType::Wechatpay,
                },
            ))),
            WalletData::AliPayRedirect(_) => {
                Ok(Self::Wallet(StripeWallet::AlipayPayment(AlipayPayment {
                    payment_method_data_type: StripePaymentMethodType::Alipay,
                })))
            }
            WalletData::CashappQr(_) => Ok(Self::Wallet(StripeWallet::Cashapp(CashappPayment {
                payment_method_data_type: StripePaymentMethodType::Cashapp,
            }))),
            WalletData::AmazonPayRedirect(_) => Ok(Self::Wallet(StripeWallet::AmazonpayPayment(
                AmazonpayPayment {
                    payment_method_types: StripePaymentMethodType::AmazonPay,
                },
            ))),
            WalletData::RevolutPay(_) => {
                Ok(Self::Wallet(StripeWallet::RevolutPay(RevolutpayPayment {
                    payment_method_types: StripePaymentMethodType::RevolutPay,
                })))
            }
            WalletData::GooglePay(gpay_data) => Ok(Self::try_from(gpay_data)?),
            WalletData::PaypalRedirect(_) | WalletData::MobilePayRedirect(_) => Err(
                ConnectorError::NotImplemented(get_unimplemented_payment_method_error_message(
                    "stripe",
                ))
                .into(),
            ),
            WalletData::AliPayQr(_)
            | WalletData::Paysera(_)
            | WalletData::BluecodeRedirect {}
            | WalletData::Skrill(_)
            | WalletData::AmazonPay(_)
            | WalletData::AliPayHkRedirect(_)
            | WalletData::MomoRedirect(_)
            | WalletData::KakaoPayRedirect(_)
            | WalletData::GoPayRedirect(_)
            | WalletData::GcashRedirect(_)
            | WalletData::ApplePayRedirect(_)
            | WalletData::ApplePayThirdPartySdk(_)
            | WalletData::DanaRedirect {}
            | WalletData::GooglePayRedirect(_)
            | WalletData::GooglePayThirdPartySdk(_)
            | WalletData::MbWayRedirect(_)
            | WalletData::PaypalSdk(_)
            | WalletData::Paze(_)
            | WalletData::SamsungPay(_)
            | WalletData::TwintRedirect {}
            | WalletData::VippsRedirect {}
            | WalletData::TouchNGoRedirect(_)
            | WalletData::SwishQr(_)
            | WalletData::WeChatPayRedirect(_)
            | WalletData::Mifinity(_) => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        }
    }
}

impl TryFrom<&BankRedirectData> for StripePaymentMethodData {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(bank_redirect_data: &BankRedirectData) -> Result<Self, Self::Error> {
        let payment_method_data_type = StripePaymentMethodType::try_from(bank_redirect_data)?;
        match bank_redirect_data {
            BankRedirectData::BancontactCard { .. } => Ok(Self::BankRedirect(
                StripeBankRedirectData::StripeBancontactCard(Box::new(StripeBancontactCard {
                    payment_method_data_type,
                })),
            )),
            BankRedirectData::Blik { blik_code } => Ok(Self::BankRedirect(
                StripeBankRedirectData::StripeBlik(Box::new(StripeBlik {
                    payment_method_data_type,
                    code: Secret::new(blik_code.clone().ok_or(
                        ConnectorError::MissingRequiredField {
                            field_name: "blik_code",
                        },
                    )?),
                })),
            )),
            BankRedirectData::Eps { bank_name, .. } => Ok(Self::BankRedirect(
                StripeBankRedirectData::StripeEps(Box::new(StripeEps {
                    payment_method_data_type,
                    bank_name: bank_name
                        .map(|bank_name| StripeBankNames::try_from(&bank_name))
                        .transpose()?,
                })),
            )),
            BankRedirectData::Giropay { .. } => Ok(Self::BankRedirect(
                StripeBankRedirectData::StripeGiropay(Box::new(StripeGiropay {
                    payment_method_data_type,
                })),
            )),
            BankRedirectData::Ideal { bank_name, .. } => {
                let bank_name = bank_name
                    .map(|bank_name| StripeBankNames::try_from(&bank_name))
                    .transpose()?;
                Ok(Self::BankRedirect(StripeBankRedirectData::StripeIdeal(
                    Box::new(StripeIdeal {
                        payment_method_data_type,
                        ideal_bank_name: bank_name,
                    }),
                )))
            }
            BankRedirectData::Przelewy24 { bank_name, .. } => {
                let bank_name = bank_name
                    .map(|bank_name| StripeBankNames::try_from(&bank_name))
                    .transpose()?;
                Ok(Self::BankRedirect(
                    StripeBankRedirectData::StripePrezelewy24(Box::new(StripePrezelewy24 {
                        payment_method_data_type,
                        bank_name,
                    })),
                ))
            }
            BankRedirectData::OnlineBankingFpx { .. } => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
            BankRedirectData::Bizum {}
            | BankRedirectData::Eft { .. }
            | BankRedirectData::Interac { .. }
            | BankRedirectData::OnlineBankingCzechRepublic { .. }
            | BankRedirectData::OnlineBankingFinland { .. }
            | BankRedirectData::OnlineBankingPoland { .. }
            | BankRedirectData::OnlineBankingSlovakia { .. }
            | BankRedirectData::OnlineBankingThailand { .. }
            | BankRedirectData::OpenBankingUk { .. }
            | BankRedirectData::Sofort { .. }
            | BankRedirectData::Trustly { .. }
            | BankRedirectData::LocalBankRedirect {} => Err(ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("stripe"),
            )
            .into()),
        }
    }
}

impl TryFrom<&GooglePayWalletData> for StripePaymentMethodData {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(gpay_data: &GooglePayWalletData) -> Result<Self, Self::Error> {
        Ok(Self::Wallet(StripeWallet::GooglepayToken(GooglePayToken {
            token: Secret::new(
                gpay_data
                    .tokenization_data
                    .get_encrypted_google_pay_token()
                    .change_context(ConnectorError::MissingRequiredField {
                        field_name: "gpay wallet_token",
                    })?
                    .as_bytes()
                    .parse_struct::<StripeGpayToken>("StripeGpayToken")
                    .change_context(ConnectorError::InvalidWalletToken {
                        wallet_name: "Google Pay".to_string(),
                    })?
                    .id,
            ),
            payment_type: StripePaymentMethodType::Card,
        })))
    }
}

impl TryFrom<(&PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntentRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(data: (&PaymentsAuthorizeRouterData, MinorUnit)) -> Result<Self, Self::Error> {
        let item = data.0;

        let mandate_metadata = item
            .request
            .mandate_id
            .as_ref()
            .and_then(|mandate_id| mandate_id.mandate_reference_id.as_ref())
            .and_then(|reference_id| match reference_id {
                payments::MandateReferenceId::ConnectorMandateId(mandate_data) => {
                    Some(mandate_data.get_mandate_metadata())
                }
                _ => None,
            });

        let (transfer_account_id, charge_type, application_fees) = if let Some(secret_value) =
            mandate_metadata.as_ref().and_then(|s| s.as_ref())
        {
            let json_value = secret_value.clone().expose();

            let parsed: Result<StripeSplitPaymentRequest, _> = serde_json::from_value(json_value);


pub struct KatapultRefundRequest {
    pub amount: StringMinorUnit,
}