    ds_response: DsResponse,
    capture_method: Option<enums::CaptureMethod>,
) -> Result<enums::AttemptStatus, error_stack::Report<errors::ConnectorError>> {
    // Redsys consistently provides a 4-digit response code, where numbers ranging from 0000 to 0099 indicate successful transactions
    if ds_response.0.starts_with("00") {
        match capture_method {
            Some(enums::CaptureMethod::Automatic) | None => Ok(enums::AttemptStatus::Charged),
            Some(enums::CaptureMethod::Manual) => Ok(enums::AttemptStatus::Authorized),
            _ => Err(errors::ConnectorError::CaptureMethodNotSupported.into()),
        }
    } else {
        match ds_response.0.as_str() {
            "0900" => Ok(enums::AttemptStatus::Charged),
            "0400" => Ok(enums::AttemptStatus::Voided),
            "0950" => Ok(enums::AttemptStatus::VoidFailed),
            "9998" => Ok(enums::AttemptStatus::AuthenticationPending),
            "9256" | "9257" | "0184" => Ok(enums::AttemptStatus::AuthenticationFailed),
            "0101" | "0102" | "0106" | "0125" | "0129" | "0172" | "0173" | "0174" | "0180"
            | "0190" | "0191" | "0195" | "0202" | "0904" | "0909" | "0913" | "0944" | "9912"
            | "0912" | "9064" | "9078" | "9093" | "9094" | "9104" | "9218" | "9253" | "9261"
            | "9915" | "9997" | "9999" => Ok(enums::AttemptStatus::Failure),
            error => Err(errors::ConnectorError::ResponseHandlingFailed)
                .attach_printable(format!("Received Unknown Status:{error}"))?,
        }
    }
}

impl TryFrom<&RedsysRouterData<&PaymentsAuthorizeRouterData>> for RedsysTransaction {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &RedsysRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        if !item.router_data.is_three_ds() {
            Err(errors::ConnectorError::NotSupported {
                message: "No-3DS cards".to_string(),
                connector: "redsys",
            })?
        };
        let auth = RedsysAuthType::try_from(&item.router_data.connector_auth_type)?;
        let ds_merchant_transactiontype = if item.router_data.request.is_auto_capture()? {
            RedsysTransactionType::Payment
        } else {
            RedsysTransactionType::Preauthorization
        };
        let card_data =
            RedsysCardData::try_from(&Some(item.router_data.request.payment_method_data.clone()))?;
        let (connector_meta_data, ds_merchant_order) = match &item.router_data.response {
            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(order_id),
                connector_metadata,
                ..
            }) => (connector_metadata.clone(), order_id.clone()),
            _ => Err(errors::ConnectorError::ResponseHandlingFailed)?,
        };
        let threeds_meta_data =
            connector_utils::to_connector_meta::<ThreeDsInvokeExempt>(connector_meta_data.clone())?;
        let emv3ds_data = EmvThreedsData::new(RedsysThreeDsInfo::AuthenticationData)
            .set_three_d_s_server_trans_i_d(threeds_meta_data.three_d_s_server_trans_i_d)
            .set_protocol_version(threeds_meta_data.message_version)
            .set_notification_u_r_l(item.router_data.request.get_complete_authorize_url()?)
            .add_browser_data(item.router_data.request.get_browser_info()?)?
            .set_three_d_s_comp_ind(ThreeDSCompInd::N)
            .set_billing_data(item.router_data.get_optional_billing())?
            .set_shipping_data(item.router_data.get_optional_shipping())?;

        let payment_authorize_request = PaymentsRequest {
            ds_merchant_emv3ds: Some(emv3ds_data),
            ds_merchant_transactiontype,
            ds_merchant_currency: item.currency.iso_4217().to_owned(),
            ds_merchant_pan: card_data.card_number,
            ds_merchant_merchantcode: auth.merchant_id.clone(),
            ds_merchant_terminal: auth.terminal_id.clone(),
            ds_merchant_order,
            ds_merchant_amount: item.amount.clone(),
            ds_merchant_expirydate: card_data.expiry_date,
            ds_merchant_cvv2: card_data.cvv2,
        };
        Self::try_from((&payment_authorize_request, &auth))
    }
}

fn build_threeds_form(ds_emv3ds: &RedsysEmv3DSData) -> Result<RedirectForm, Error> {
    let creq = ds_emv3ds
        .creq
        .clone()
        .ok_or(errors::ConnectorError::ResponseDeserializationFailed)?;

    let endpoint = ds_emv3ds
        .acs_u_r_l
        .clone()
        .ok_or(errors::ConnectorError::ResponseDeserializationFailed)?;

    let mut form_fields = std::collections::HashMap::new();
    form_fields.insert("creq".to_string(), creq);

    Ok(RedirectForm::Form {
        endpoint,
        method: common_utils::request::Method::Post,
        form_fields,
    })
}

impl<F> TryFrom<ResponseRouterData<F, RedsysResponse, PaymentsAuthorizeData, PaymentsResponseData>>
    for RouterData<F, PaymentsAuthorizeData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, RedsysResponse, PaymentsAuthorizeData, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let (response, status) = match item.response.clone() {
            RedsysResponse::RedsysResponse(transaction_response) => {
                let connector_metadata = match item.data.response {
                    Ok(PaymentsResponseData::TransactionResponse {
                        connector_metadata, ..
                    }) => connector_metadata,
                    _ => None,
                };
                let response_data: RedsysPaymentsResponse = to_connector_response_data(
                    &transaction_response.ds_merchant_parameters.clone().expose(),
                )?;
                get_payments_response(
                    response_data,
                    item.data.request.capture_method,
                    connector_metadata,
                    item.http_code,
                )?
            }
            RedsysResponse::RedsysErrorResponse(response) => {
                let response = Err(ErrorResponse {
                    code: response.error_code.clone(),
                    message: response.error_code.clone(),
                    reason: Some(response.error_code.clone()),
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                });

                (response, enums::AttemptStatus::Failure)
            }
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ThreedsChallengeResponse {
    cres: String,
}

impl TryFrom<&RedsysRouterData<&PaymentsCompleteAuthorizeRouterData>> for RedsysTransaction {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &RedsysRouterData<&PaymentsCompleteAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        if !item.router_data.is_three_ds() {
            Err(errors::ConnectorError::NotSupported {
                message: "PaymentsComplete flow for no-3ds cards".to_string(),
                connector: "redsys",
            })?
        };
        let card_data =
            RedsysCardData::try_from(&item.router_data.request.payment_method_data.clone())?;
        let auth = RedsysAuthType::try_from(&item.router_data.connector_auth_type)?;
        let redirect_response = item
            .router_data
            .request
            .get_redirect_response_payload()
            .ok()
            .clone()
            .map(|payload_data| {
                payload_data
                    .parse_value::<ThreedsChallengeResponse>("Redsys ThreedsChallengeResponse")
                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)
            })
            .transpose()?;
        let billing_data = item.router_data.get_optional_billing();
        let shipping_data = item.router_data.get_optional_shipping();

        let emv3ds_data = match redirect_response {
            Some(payload) => {
                if let Ok(threeds_invoke_meta_data) =
                    connector_utils::to_connector_meta::<RedsysThreeDsInvokeData>(
                        item.router_data.request.connector_meta.clone(),
                    )
                {
                    EmvThreedsData::new(RedsysThreeDsInfo::ChallengeResponse)
                        .set_protocol_version(threeds_invoke_meta_data.message_version)
                        .set_three_d_s_cres(payload.cres)
                        .set_billing_data(billing_data)?
                        .set_shipping_data(shipping_data)?
                } else if let Ok(threeds_meta_data) =
                    connector_utils::to_connector_meta::<ThreeDsInvokeExempt>(
                        item.router_data.request.connector_meta.clone(),
                    )
                {
                    EmvThreedsData::new(RedsysThreeDsInfo::ChallengeResponse)
                        .set_protocol_version(threeds_meta_data.message_version)
                        .set_three_d_s_cres(payload.cres)
                        .set_billing_data(billing_data)?
                        .set_shipping_data(shipping_data)?
                } else {
                    Err(errors::ConnectorError::RequestEncodingFailed)?
                }
            }
            None => {
                if let Ok(threeds_invoke_meta_data) =
                    connector_utils::to_connector_meta::<RedsysThreeDsInvokeData>(
                        item.router_data.request.connector_meta.clone(),
                    )
                {
                    let three_d_s_comp_ind = ThreeDSCompInd::from(
                        item.router_data.request.get_threeds_method_comp_ind()?,
                    );
                    let browser_info = item.router_data.request.get_browser_info()?;
                    let complete_authorize_url =
                        item.router_data.request.get_complete_authorize_url()?;
                    EmvThreedsData::new(RedsysThreeDsInfo::AuthenticationData)
                        .set_three_d_s_server_trans_i_d(
                            threeds_invoke_meta_data.directory_server_id,
                        )
                        .set_protocol_version(threeds_invoke_meta_data.message_version)
                        .set_three_d_s_comp_ind(three_d_s_comp_ind)
                        .add_browser_data(browser_info)?
                        .set_notification_u_r_l(complete_authorize_url)
                        .set_billing_data(billing_data)?
                        .set_shipping_data(shipping_data)?
                } else {
                    Err(errors::ConnectorError::NoConnectorMetaData)?
                }
            }
        };

        let ds_merchant_transactiontype = if item.router_data.request.is_auto_capture()? {
            RedsysTransactionType::Payment
        } else {
            RedsysTransactionType::Preauthorization
        };
        let ds_merchant_order = item
            .router_data
            .request
            .connector_transaction_id
            .clone()
            .ok_or(errors::ConnectorError::RequestEncodingFailed)
            .attach_printable("Missing connector_transaction_id")?;

        let complete_authorize_response = PaymentsRequest {
            ds_merchant_emv3ds: Some(emv3ds_data),
            ds_merchant_transactiontype,
            ds_merchant_currency: item.currency.iso_4217().to_owned(),
            ds_merchant_pan: card_data.card_number,
            ds_merchant_merchantcode: auth.merchant_id.clone(),
            ds_merchant_terminal: auth.terminal_id.clone(),
            ds_merchant_order,
            ds_merchant_amount: item.amount.clone(),
            ds_merchant_expirydate: card_data.expiry_date,
            ds_merchant_cvv2: card_data.cvv2,
        };
        Self::try_from((&complete_authorize_response, &auth))
    }
}

impl<F> TryFrom<ResponseRouterData<F, RedsysResponse, CompleteAuthorizeData, PaymentsResponseData>>
    for RouterData<F, CompleteAuthorizeData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, RedsysResponse, CompleteAuthorizeData, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let (response, status) = match item.response.clone() {
            RedsysResponse::RedsysResponse(transaction_response) => {
                let response_data: RedsysPaymentsResponse = to_connector_response_data(
                    &transaction_response.ds_merchant_parameters.clone().expose(),
                )?;

                get_payments_response(
                    response_data,
                    item.data.request.capture_method,
                    None,
                    item.http_code,
                )?
            }
            RedsysResponse::RedsysErrorResponse(response) => {
                let response = Err(ErrorResponse {
                    code: response.error_code.clone(),
                    message: response.error_code.clone(),
                    reason: Some(response.error_code.clone()),
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                });

                (response, enums::AttemptStatus::Failure)
            }
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

impl From<api_models::payments::ThreeDsCompletionIndicator> for ThreeDSCompInd {
    fn from(threeds_compl_flag: api_models::payments::ThreeDsCompletionIndicator) -> Self {
        match threeds_compl_flag {
            api_models::payments::ThreeDsCompletionIndicator::Success => Self::Y,
            api_models::payments::ThreeDsCompletionIndicator::Failure
            | api_models::payments::ThreeDsCompletionIndicator::NotAvailable => Self::N,
        }
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct RedsysOperationRequest {
    ds_merchant_order: String,
    ds_merchant_merchantcode: Secret<String>,
    ds_merchant_terminal: Secret<String>,
    ds_merchant_currency: String,
    ds_merchant_transactiontype: RedsysTransactionType,
    ds_merchant_amount: StringMinorUnit,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RedsysOperationsResponse {
    #[serde(rename = "Ds_Order")]
    ds_order: String,
    #[serde(rename = "Ds_Response")]
    ds_response: DsResponse,
    #[serde(rename = "Ds_AuthorisationCode")]
    ds_authorisation_code: Option<String>,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum RedsysOperationResponse {
    RedsysOperationResponse(RedsysTransaction),
    RedsysOperationsErrorResponse(RedsysErrorResponse),
}

impl TryFrom<&RedsysRouterData<&PaymentsCaptureRouterData>> for RedsysTransaction {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &RedsysRouterData<&PaymentsCaptureRouterData>) -> Result<Self, Self::Error> {
        let auth = RedsysAuthType::try_from(&item.router_data.connector_auth_type)?;
        let redys_capture_request = RedsysOperationRequest {
            ds_merchant_order: item.router_data.request.connector_transaction_id.clone(),
            ds_merchant_merchantcode: auth.merchant_id.clone(),
            ds_merchant_terminal: auth.terminal_id.clone(),
            ds_merchant_currency: item.router_data.request.currency.iso_4217().to_owned(),
            ds_merchant_transactiontype: RedsysTransactionType::Confirmation,
            ds_merchant_amount: item.amount.clone(),
        };
        Self::try_from((&redys_capture_request, &auth))
    }
}

impl<F> TryFrom<ResponseRouterData<F, RedsysResponse, PaymentsCaptureData, PaymentsResponseData>>
    for RouterData<F, PaymentsCaptureData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, RedsysResponse, PaymentsCaptureData, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let (response, status) = match item.response {
            RedsysResponse::RedsysResponse(redsys_transaction_response) => {
                let response_data: RedsysOperationsResponse = to_connector_response_data(
                    &redsys_transaction_response
                        .ds_merchant_parameters
                        .clone()
                        .expose(),
                )?;
                let status = get_redsys_attempt_status(response_data.ds_response.clone(), None)?;

                let response = if connector_utils::is_payment_failure(status) {
                    Err(ErrorResponse {
                        code: response_data.ds_response.0.clone(),
                        message: response_data.ds_response.0.clone(),
                        reason: Some(response_data.ds_response.0.clone()),
                        status_code: item.http_code,
                        attempt_status: None,
                        connector_transaction_id: Some(response_data.ds_order.clone()),
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(
                            response_data.ds_order.clone(),
                        ),
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: Some(response_data.ds_order.clone()),
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                };
                (response, status)
            }
            RedsysResponse::RedsysErrorResponse(error_response) => {
                let response = Err(ErrorResponse {
                    code: error_response.error_code.clone(),
                    message: error_response.error_code.clone(),
                    reason: Some(error_response.error_code.clone()),
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                });
                (response, enums::AttemptStatus::Failure)
            }
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

impl TryFrom<&RedsysRouterData<&PaymentsCancelRouterData>> for RedsysTransaction {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &RedsysRouterData<&PaymentsCancelRouterData>) -> Result<Self, Self::Error> {
        let auth = RedsysAuthType::try_from(&item.router_data.connector_auth_type)?;
        let redsys_cancel_request = RedsysOperationRequest {
            ds_merchant_order: item.router_data.request.connector_transaction_id.clone(),
            ds_merchant_merchantcode: auth.merchant_id.clone(),
            ds_merchant_terminal: auth.terminal_id.clone(),
            ds_merchant_currency: item.currency.iso_4217().to_owned(),
            ds_merchant_transactiontype: RedsysTransactionType::Cancellation,
            ds_merchant_amount: item.amount.clone(),
        };
        Self::try_from((&redsys_cancel_request, &auth))
    }
}

impl<F> TryFrom<&RedsysRouterData<&RefundsRouterData<F>>> for RedsysTransaction {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &RedsysRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let auth = RedsysAuthType::try_from(&item.router_data.connector_auth_type)?;
        let redsys_refund_request = RedsysOperationRequest {
            ds_merchant_order: item.router_data.request.connector_transaction_id.clone(),
            ds_merchant_merchantcode: auth.merchant_id.clone(),
            ds_merchant_terminal: auth.terminal_id.clone(),
            ds_merchant_currency: item.currency.iso_4217().to_owned(),
            ds_merchant_transactiontype: RedsysTransactionType::Refund,
            ds_merchant_amount: item.amount.clone(),
        };
        Self::try_from((&redsys_refund_request, &auth))
    }
}

impl<F> TryFrom<ResponseRouterData<F, RedsysResponse, PaymentsCancelData, PaymentsResponseData>>
    for RouterData<F, PaymentsCancelData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, RedsysResponse, PaymentsCancelData, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let (response, status) = match item.response {
            RedsysResponse::RedsysResponse(redsys_transaction_response) => {
                let response_data: RedsysOperationsResponse = to_connector_response_data(
                    &redsys_transaction_response
                        .ds_merchant_parameters
                        .clone()
                        .expose(),
                )?;

                let status = get_redsys_attempt_status(response_data.ds_response.clone(), None)?;

                let response = if connector_utils::is_payment_failure(status) {
                    Err(ErrorResponse {
                        code: response_data.ds_response.0.clone(),
                        message: response_data.ds_response.0.clone(),
                        reason: Some(response_data.ds_response.0.clone()),
                        status_code: item.http_code,
                        attempt_status: None,
                        connector_transaction_id: Some(response_data.ds_order.clone()),
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(
                            response_data.ds_order.clone(),
                        ),
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: Some(response_data.ds_order.clone()),
                        incremental_authorization_allowed: None,
                        charges: None,
                    })
                };
                (response, status)
            }
            RedsysResponse::RedsysErrorResponse(error_response) => {
                let response = Err(ErrorResponse {
                    code: error_response.error_code.clone(),
                    message: error_response.error_code.clone(),
                    reason: Some(error_response.error_code.clone()),
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                });

                (response, enums::AttemptStatus::VoidFailed)
            }
        };
        Ok(Self {
            status,
            response,
            ..item.data
        })
    }
}

impl TryFrom<DsResponse> for enums::RefundStatus {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(ds_response: DsResponse) -> Result<Self, Self::Error> {
        match ds_response.0.as_str() {
            "0900" => Ok(Self::Success),
            "9999" => Ok(Self::Pending),
            "0950" | "0172" | "174" => Ok(Self::Failure),
            unknown_status => Err(errors::ConnectorError::ResponseHandlingFailed)
                .attach_printable(format!("Received unknown status:{unknown_status}"))?,
        }
    }
}

impl TryFrom<RefundsResponseRouterData<Execute, RedsysResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RedsysResponse>,
    ) -> Result<Self, Self::Error> {
        let response = match item.response {
            RedsysResponse::RedsysResponse(redsys_transaction) => {
                let response_data: RedsysOperationsResponse = to_connector_response_data(
                    &redsys_transaction.ds_merchant_parameters.clone().expose(),
                )?;

                let refund_status =
                    enums::RefundStatus::try_from(response_data.ds_response.clone())?;

                if connector_utils::is_refund_failure(refund_status) {
                    Err(ErrorResponse {
                        code: response_data.ds_response.0.clone(),
                        message: response_data.ds_response.0.clone(),
                        reason: Some(response_data.ds_response.0.clone()),
                        status_code: item.http_code,
                        attempt_status: None,
                        connector_transaction_id: None,
                        network_advice_code: None,
                        network_decline_code: None,
                        network_error_message: None,
                        connector_metadata: None,
                    })
                } else {
                    Ok(RefundsResponseData {
                        connector_refund_id: response_data.ds_order,
                        refund_status,
                    })
                }
            }
            RedsysResponse::RedsysErrorResponse(redsys_error_response) => Err(ErrorResponse {
                code: redsys_error_response.error_code.clone(),
                message: redsys_error_response.error_code.clone(),
                reason: Some(redsys_error_response.error_code.clone()),
                status_code: item.http_code,
                attempt_status: None,
                connector_transaction_id: None,
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            }),
        };

        Ok(Self {
            response,
            ..item.data
        })
    }
}

fn get_payments_response(
    redsys_payments_response: RedsysPaymentsResponse,
    capture_method: Option<enums::CaptureMethod>,
    connector_metadata: Option<josekit::Value>,
    http_code: u16,
) -> Result<
    (
        Result<PaymentsResponseData, ErrorResponse>,
        enums::AttemptStatus,
    ),
    Error,
> {
    if let Some(ds_response) = redsys_payments_response.ds_response {
        let status = get_redsys_attempt_status(ds_response.clone(), capture_method)?;
        let response = if connector_utils::is_payment_failure(status) {
            Err(ErrorResponse {
                code: ds_response.0.clone(),
                message: ds_response.0.clone(),
                reason: Some(ds_response.0.clone()),
                status_code: http_code,
                attempt_status: None,
                connector_transaction_id: Some(redsys_payments_response.ds_order.clone()),
                network_advice_code: None,
                network_decline_code: None,
                network_error_message: None,
                connector_metadata: None,
            })
        } else {
            Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    redsys_payments_response.ds_order.clone(),
                ),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata,
                network_txn_id: None,
                connector_response_reference_id: Some(redsys_payments_response.ds_order.clone()),
                incremental_authorization_allowed: None,
                charges: None,
            })
        };

        Ok((response, status))
    } else {
        let redirection_form = redsys_payments_response
            .ds_emv3ds
            .map(|ds_emv3ds| build_threeds_form(&ds_emv3ds))
            .transpose()?;
        let response = Ok(PaymentsResponseData::TransactionResponse {
            resource_id: ResponseId::ConnectorTransactionId(
                redsys_payments_response.ds_order.clone(),
            ),
            redirection_data: Box::new(redirection_form),
            mandate_reference: Box::new(None),
            connector_metadata,
            network_txn_id: None,
            connector_response_reference_id: Some(redsys_payments_response.ds_order.clone()),
            incremental_authorization_allowed: None,
            charges: None,
        });

        Ok((response, enums::AttemptStatus::AuthenticationPending))
    }
}

#[derive(Debug, Serialize)]
pub struct Messages {
    #[serde(rename = "Version")]
    version: VersionData,
    #[serde(rename = "Signature")]
    signature: String,
    #[serde(rename = "SignatureVersion")]
    signature_version: String,
}

#[derive(Debug, Serialize)]
#[serde(rename = "Version")]
pub struct VersionData {
    #[serde(rename = "@Ds_Version")]
    ds_version: String,
    #[serde(rename = "Message")]
    message: Message,
}

#[derive(Debug, Serialize)]
pub struct Message {
    #[serde(rename = "Transaction")]
    transaction: RedsysSyncRequest,
}

#[derive(Debug, Serialize)]
#[serde(rename = "Transaction")]
pub struct RedsysSyncRequest {
    #[serde(rename = "Ds_MerchantCode")]
    ds_merchant_code: Secret<String>,
    #[serde(rename = "Ds_Terminal")]
    ds_terminal: Secret<String>,
    #[serde(rename = "Ds_Order")]
    ds_order: String,
    #[serde(rename = "Ds_TransactionType")]
    ds_transaction_type: String,
}

fn get_transaction_type(
    status: enums::AttemptStatus,
    capture_method: Option<enums::CaptureMethod>,
) -> Result<String, errors::ConnectorError> {
    match status {
        enums::AttemptStatus::AuthenticationPending
        | enums::AttemptStatus::AuthenticationSuccessful
        | enums::AttemptStatus::Started
        | enums::AttemptStatus::Authorizing
        | enums::AttemptStatus::Authorized
        | enums::AttemptStatus::DeviceDataCollectionPending => match capture_method {
            Some(enums::CaptureMethod::Automatic) | None => {
                Ok(transaction_type::PAYMENT.to_owned())
            }
            Some(enums::CaptureMethod::Manual) => Ok(transaction_type::PREAUTHORIZATION.to_owned()),
            Some(capture_method) => Err(errors::ConnectorError::NotSupported {
                message: capture_method.to_string(),
                connector: "redsys",
            }),
        },
        enums::AttemptStatus::VoidInitiated => Ok(transaction_type::CANCELLATION.to_owned()),
        enums::AttemptStatus::PartialChargedAndChargeable
        | enums::AttemptStatus::CaptureInitiated => Ok(transaction_type::CONFIRMATION.to_owned()),
        enums::AttemptStatus::Pending => match capture_method {
            Some(enums::CaptureMethod::Automatic) | None => {
                Ok(transaction_type::PAYMENT.to_owned())
            }
            Some(enums::CaptureMethod::Manual) => Ok(transaction_type::CONFIRMATION.to_owned()),
            Some(capture_method) => Err(errors::ConnectorError::NotSupported {
                message: capture_method.to_string(),
                connector: "redsys",
            }),
        },
        other_attempt_status => Err(errors::ConnectorError::NotSupported {
            message: format!("Payment sync after terminal status: {other_attempt_status} payment"),
            connector: "redsys",
        }),
    }
}

fn construct_sync_request(
    order_id: String,
    transaction_type: String,
    auth: RedsysAuthType,
) -> Result<Vec<u8>, Error> {
    let transaction_data = RedsysSyncRequest {
        ds_merchant_code: auth.merchant_id,
        ds_terminal: auth.terminal_id,
        ds_transaction_type: transaction_type,
        ds_order: order_id.clone(),
    };
    let version = VersionData {
        ds_version: DS_VERSION.to_owned(),
        message: Message {
            transaction: transaction_data,
        },
    };
    let version_data = quick_xml::se::to_string(&version)
        .change_context(errors::ConnectorError::RequestEncodingFailed)?;

    let signature = get_signature(&order_id, &version_data, auth.sha256_pwd.peek())?;

    let messages = Messages {
        version,
        signature,
        signature_version: SIGNATURE_VERSION.to_owned(),
    };

    let cdata = quick_xml::se::to_string(&messages)
        .change_context(errors::ConnectorError::RequestEncodingFailed)?;

    let body = format!(
        r#"<soapenv:Envelope xmlns:soapenv="{}" xmlns:web="{}"><soapenv:Header/><soapenv:Body><web:consultaOperaciones><cadenaXML><![CDATA[{}]]></cadenaXML></web:consultaOperaciones></soapenv:Body></soapenv:Envelope>"#,
        common_utils::consts::SOAP_ENV_NAMESPACE,
        XMLNS_WEB_URL,
        cdata
    );

    Ok(body.as_bytes().to_vec())
}

pub fn build_payment_sync_request(item: &PaymentsSyncRouterData) -> Result<Vec<u8>, Error> {
    let transaction_type = get_transaction_type(item.status, item.request.capture_method)?;
    let auth = RedsysAuthType::try_from(&item.connector_auth_type)?;
    let connector_transaction_id = item
        .request
        .connector_transaction_id
        .get_connector_transaction_id()
        .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
    construct_sync_request(connector_transaction_id, transaction_type, auth)
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename = "soapenv:envelope")]
pub struct RedsysSyncResponse {
    #[serde(rename = "@xmlns:soapenv")]
    xmlns_soapenv: String,
    #[serde(rename = "@xmlns:soapenc")]
    xmlns_soapenc: String,
    #[serde(rename = "@xmlns:xsd")]
    xmlns_xsd: String,
    #[serde(rename = "@xmlns:xsi")]
    xmlns_xsi: String,
    #[serde(rename = "header")]
    header: Option<SoapHeader>,
    #[serde(rename = "body")]
    body: SyncResponseBody,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SoapHeader {}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct SyncResponseBody {
    consultaoperacionesresponse: ConsultaOperacionesResponse,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct ConsultaOperacionesResponse {
    #[serde(rename = "@xmlns:p259")]
    xmlns_p259: String,
    consultaoperacionesreturn: ConsultaOperacionesReturn,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct ConsultaOperacionesReturn {
    messages: MessagesResponseData,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct MessagesResponseData {
    version: VersionResponseData,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub struct VersionResponseData {
    #[serde(rename = "@ds_version")]
    ds_version: String,
    message: MessageResponseType,
}

// The response will contain either a sync transaction data or error data.
// Since the XML parser does not support enums for this case, we use Option to handle both scenarios.
// If both are present or both are absent, an error is thrown.
#[derive(Debug, Serialize, Deserialize)]
pub struct MessageResponseType {
    response: Option<RedsysSyncResponseData>,
    errormsg: Option<SyncErrorCode>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SyncErrorCode {
    ds_errorcode: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RedsysSyncResponseData {
    ds_order: String,
    ds_transactiontype: String,
    ds_amount: Option<String>,
    ds_currency: Option<String>,
    ds_securepayment: Option<String>,
    ds_state: Option<String>,
    ds_response: Option<DsResponse>,
}

impl<F> TryFrom<ResponseRouterData<F, RedsysSyncResponse, PaymentsSyncData, PaymentsResponseData>>
    for RouterData<F, PaymentsSyncData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: ResponseRouterData<F, RedsysSyncResponse, PaymentsSyncData, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let message_data = item
            .response
            .body
            .consultaoperacionesresponse
            .consultaoperacionesreturn
            .messages
            .version
            .message;
        let (status, response) = match (message_data.response, message_data.errormsg) {
            (Some(response), None) => {
                if let Some(ds_response) = response.ds_response {
                    let status = get_redsys_attempt_status(
                        ds_response.clone(),
                        item.data.request.capture_method,
                    )?;

                    if connector_utils::is_payment_failure(status) {
                        let payment_response = Err(ErrorResponse {
                            status_code: item.http_code,
                            code: ds_response.0.clone(),
                            message: ds_response.0.clone(),
                            reason: Some(ds_response.0.clone()),
                            attempt_status: None,
                            connector_transaction_id: None,
                            network_advice_code: None,
                            network_decline_code: None,
                            network_error_message: None,
                            connector_metadata: None,
                        });
                        (status, payment_response)
                    } else {
                        let payment_response = Ok(PaymentsResponseData::TransactionResponse {
                            resource_id: ResponseId::ConnectorTransactionId(
                                response.ds_order.clone(),
                            ),
                            redirection_data: Box::new(None),
                            mandate_reference: Box::new(None),
                            connector_metadata: None,
                            network_txn_id: None,
                            connector_response_reference_id: Some(response.ds_order.clone()),
                            incremental_authorization_allowed: None,
                            charges: None,
                        });
                        (status, payment_response)
                    }
                } else {
                    // When the payment is in authentication or still processing
                    let payment_response = Ok(PaymentsResponseData::TransactionResponse {
                        resource_id: ResponseId::ConnectorTransactionId(response.ds_order.clone()),
                        redirection_data: Box::new(None),
                        mandate_reference: Box::new(None),
                        connector_metadata: None,
                        network_txn_id: None,
                        connector_response_reference_id: Some(response.ds_order.clone()),
                        incremental_authorization_allowed: None,
                        charges: None,
                    });

                    (item.data.status, payment_response)
                }
            }
            (None, Some(errormsg)) => {
                let error_code = errormsg.ds_errorcode.clone();
                let response = Err(ErrorResponse {
                    code: error_code.clone(),
                    message: error_code.clone(),
                    reason: Some(error_code),
                    status_code: item.http_code,
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                    connector_metadata: None,
                });
                (item.data.status, response)
            }
            (Some(_), Some(_)) | (None, None) => {
                Err(errors::ConnectorError::ResponseHandlingFailed)?
            }
        };

        Ok(Self {
            status,
            response,


// Struct: Retrieve
// File: crates/hyperswitch_domain_models/src/router_flow_types/files.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct Retrieve


// Function: insert
// File: crates/diesel_models/src/query/fraud_check.rs
// Module: diesel_models

pub fn insert(self, conn: &PgPooledConn) -> StorageResult<FraudCheck>


// Implementation: impl api::ConnectorAccessToken for for Mollie
// File: crates/hyperswitch_connectors/src/connectors/mollie.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ConnectorAccessToken for for Mollie


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/datatrans.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Implementation: impl api::RefundSync for for Bluesnap
// File: crates/hyperswitch_connectors/src/connectors/bluesnap.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundSync for for Bluesnap


// Struct: Response
// File: crates/hyperswitch_interfaces/src/types.rs
// Module: hyperswitch_interfaces
// Implementations: 0

pub struct Response


// Function: log_refund_delete
// File: crates/router/src/services/kafka.rs
// Module: router

pub fn log_refund_delete(
        &self,
        delete_old_refund: &Refund,
        tenant_id: TenantID,
    ) -> MQResult<()>


// File: crates/connector_configs/src/lib.rs
// Module: connector_configs

pub mod common_config;
pub mod connector;
pub mod response_modifier;
pub mod transformer;


// Function: get_org_auth_events_filters
// File: crates/router/src/analytics.rs
// Module: router

pub fn get_org_auth_events_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetAuthEventFilterRequest>,
    ) -> impl Responder


// Struct: Celero
// File: crates/hyperswitch_connectors/src/connectors/celero.rs
// Module: hyperswitch_connectors
// Implementations: 17
// Traits: api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, ConnectorCommon, ConnectorValidation, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Celero

api::Payment api::PaymentSession api::ConnectorAccessToken api::MandateSetup api::PaymentAuthorize api::PaymentSync api::PaymentCapture api::PaymentVoid api::Refund api::RefundExecute api::RefundSync api::PaymentToken ConnectorCommon ConnectorValidation webhooks::IncomingWebhook ConnectorSpecifications


// Struct: PayoutActionRequest
// File: crates/api_models/src/payouts.rs
// Module: api_models
// Implementations: 0

pub struct PayoutActionRequest


// Struct: SquareRefundResponseDetails
// File: crates/hyperswitch_connectors/src/connectors/square/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SquareRefundResponseDetails


// Struct: Transaction
// File: crates/hyperswitch_domain_models/src/router_flow_types/fraud_check.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct Transaction


// Implementation: impl api::PaymentSync for for Braintree
// File: crates/hyperswitch_connectors/src/connectors/braintree.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Braintree


// File: crates/router/tests/connectors/adyenplatform.rs
// Module: router


// Struct: PaymentAttemptListData
// File: crates/hyperswitch_domain_models/src/payments.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PaymentAttemptListData<F>


// Struct: DeutschebankTransactionInfo
// File: crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct DeutschebankTransactionInfo


// Struct: TransientDecryptDataRequest
// File: crates/common_utils/src/types/keymanager.rs
// Module: common_utils
// Implementations: 0

pub struct TransientDecryptDataRequest


// Function: from_option_secret_value
// File: crates/hyperswitch_domain_models/src/router_data.rs
// Module: hyperswitch_domain_models

pub fn from_option_secret_value(
        value: Option<common_utils::pii::SecretSerdeValue>,
    ) -> common_utils::errors::CustomResult<Self, common_utils::errors::ParsingError>


// Struct: ProxyRequest
// File: crates/api_models/src/proxy.rs
// Module: api_models
// Implementations: 1
// Traits: common_utils::events::ApiEventMetric

pub struct ProxyRequest


// File: crates/hyperswitch_domain_models/src/merchant_account.rs
// Module: hyperswitch_domain_models
// Public functions: 7
// Public structs: 4

use common_utils::{
    crypto::{OptionalEncryptableName, OptionalEncryptableValue},
    date_time,
    encryption::Encryption,
    errors::{CustomResult, ValidationError},
    ext_traits::ValueExt,
    pii, type_name,
    types::keymanager::{self},
};
use diesel_models::{
    enums::MerchantStorageScheme, merchant_account::MerchantAccountUpdateInternal,
};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use router_env::logger;

use crate::type_encryption::{crypto_operation, AsyncLift, CryptoOperation};

#[cfg(feature = "v1")]
#[derive(Clone, Debug, serde::Serialize)]
pub struct MerchantAccount {
    merchant_id: common_utils::id_type::MerchantId,
    pub return_url: Option<String>,
    pub enable_payment_response_hash: bool,
    pub payment_response_hash_key: Option<String>,
    pub redirect_to_merchant_with_http_post: bool,
    pub merchant_name: OptionalEncryptableName,
    pub merchant_details: OptionalEncryptableValue,
    pub webhook_details: Option<diesel_models::business_profile::WebhookDetails>,
    pub sub_merchants_enabled: Option<bool>,
    pub parent_merchant_id: Option<common_utils::id_type::MerchantId>,
    pub publishable_key: String,
    pub storage_scheme: MerchantStorageScheme,
    pub locker_id: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub routing_algorithm: Option<serde_json::Value>,
    pub primary_business_details: serde_json::Value,
    pub frm_routing_algorithm: Option<serde_json::Value>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub intent_fulfillment_time: Option<i64>,
    pub payout_routing_algorithm: Option<serde_json::Value>,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub is_recon_enabled: bool,
    pub default_profile: Option<common_utils::id_type::ProfileId>,
    pub recon_status: diesel_models::enums::ReconStatus,
    pub payment_link_config: Option<serde_json::Value>,
    pub pm_collect_link_config: Option<serde_json::Value>,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

#[cfg(feature = "v1")]
#[derive(Clone)]
/// Set the private fields of merchant account
pub struct MerchantAccountSetter {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub return_url: Option<String>,
    pub enable_payment_response_hash: bool,
    pub payment_response_hash_key: Option<String>,
    pub redirect_to_merchant_with_http_post: bool,
    pub merchant_name: OptionalEncryptableName,
    pub merchant_details: OptionalEncryptableValue,
    pub webhook_details: Option<diesel_models::business_profile::WebhookDetails>,
    pub sub_merchants_enabled: Option<bool>,
    pub parent_merchant_id: Option<common_utils::id_type::MerchantId>,
    pub publishable_key: String,
    pub storage_scheme: MerchantStorageScheme,
    pub locker_id: Option<String>,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub routing_algorithm: Option<serde_json::Value>,
    pub primary_business_details: serde_json::Value,
    pub frm_routing_algorithm: Option<serde_json::Value>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub intent_fulfillment_time: Option<i64>,
    pub payout_routing_algorithm: Option<serde_json::Value>,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub is_recon_enabled: bool,
    pub default_profile: Option<common_utils::id_type::ProfileId>,
    pub recon_status: diesel_models::enums::ReconStatus,
    pub payment_link_config: Option<serde_json::Value>,
    pub pm_collect_link_config: Option<serde_json::Value>,
    pub version: common_enums::ApiVersion,
    pub is_platform_account: bool,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

#[cfg(feature = "v1")]
impl From<MerchantAccountSetter> for MerchantAccount {
    fn from(item: MerchantAccountSetter) -> Self {
        Self {
            merchant_id: item.merchant_id,
            return_url: item.return_url,
            enable_payment_response_hash: item.enable_payment_response_hash,
            payment_response_hash_key: item.payment_response_hash_key,
            redirect_to_merchant_with_http_post: item.redirect_to_merchant_with_http_post,
            merchant_name: item.merchant_name,
            merchant_details: item.merchant_details,
            webhook_details: item.webhook_details,
            sub_merchants_enabled: item.sub_merchants_enabled,
            parent_merchant_id: item.parent_merchant_id,
            publishable_key: item.publishable_key,
            storage_scheme: item.storage_scheme,
            locker_id: item.locker_id,
            metadata: item.metadata,
            routing_algorithm: item.routing_algorithm,
            primary_business_details: item.primary_business_details,
            frm_routing_algorithm: item.frm_routing_algorithm,
            created_at: item.created_at,
            modified_at: item.modified_at,
            intent_fulfillment_time: item.intent_fulfillment_time,
            payout_routing_algorithm: item.payout_routing_algorithm,
            organization_id: item.organization_id,
            is_recon_enabled: item.is_recon_enabled,
            default_profile: item.default_profile,
            recon_status: item.recon_status,
            payment_link_config: item.payment_link_config,
            pm_collect_link_config: item.pm_collect_link_config,
            version: item.version,
            is_platform_account: item.is_platform_account,
            product_type: item.product_type,
            merchant_account_type: item.merchant_account_type,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone)]
/// Set the private fields of merchant account
pub struct MerchantAccountSetter {
    pub id: common_utils::id_type::MerchantId,
    pub merchant_name: OptionalEncryptableName,
    pub merchant_details: OptionalEncryptableValue,
    pub publishable_key: String,
    pub storage_scheme: MerchantStorageScheme,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub recon_status: diesel_models::enums::ReconStatus,
    pub is_platform_account: bool,
    pub version: common_enums::ApiVersion,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

#[cfg(feature = "v2")]
impl From<MerchantAccountSetter> for MerchantAccount {
    fn from(item: MerchantAccountSetter) -> Self {
        let MerchantAccountSetter {
            id,
            merchant_name,
            merchant_details,
            publishable_key,
            storage_scheme,
            metadata,
            created_at,
            modified_at,
            organization_id,
            recon_status,
            is_platform_account,
            version,
            product_type,
            merchant_account_type,
        } = item;
        Self {
            id,
            merchant_name,
            merchant_details,
            publishable_key,
            storage_scheme,
            metadata,
            created_at,
            modified_at,
            organization_id,
            recon_status,
            is_platform_account,
            version,
            product_type,
            merchant_account_type,
        }
    }
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Serialize)]
pub struct MerchantAccount {
    id: common_utils::id_type::MerchantId,
    pub merchant_name: OptionalEncryptableName,
    pub merchant_details: OptionalEncryptableValue,
    pub publishable_key: String,
    pub storage_scheme: MerchantStorageScheme,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub created_at: time::PrimitiveDateTime,
    pub modified_at: time::PrimitiveDateTime,
    pub organization_id: common_utils::id_type::OrganizationId,
    pub recon_status: diesel_models::enums::ReconStatus,
    pub is_platform_account: bool,
    pub version: common_enums::ApiVersion,
    pub product_type: Option<common_enums::MerchantProductType>,
    pub merchant_account_type: common_enums::MerchantAccountType,
}

impl MerchantAccount {
    #[cfg(feature = "v1")]
    /// Get the unique identifier of MerchantAccount
    pub fn get_id(&self) -> &common_utils::id_type::MerchantId {
        &self.merchant_id
    }

    #[cfg(feature = "v2")]
    /// Get the unique identifier of MerchantAccount
    pub fn get_id(&self) -> &common_utils::id_type::MerchantId {
        &self.id
    }

    /// Get the organization_id from MerchantAccount
    pub fn get_org_id(&self) -> &common_utils::id_type::OrganizationId {
        &self.organization_id
    }

    /// Get the merchant_details from MerchantAccount
    pub fn get_merchant_details(&self) -> &OptionalEncryptableValue {
        &self.merchant_details
    }

    /// Extract merchant_tax_registration_id from merchant_details
    pub fn get_merchant_tax_registration_id(&self) -> Option<Secret<String>> {
        self.merchant_details.as_ref().and_then(|details| {
            details
                .get_inner()
                .peek()
                .get("merchant_tax_registration_id")
                .and_then(|id| id.as_str().map(|s| Secret::new(s.to_string())))
        })
    }

    /// Check whether the merchant account is a platform account
    pub fn is_platform_account(&self) -> bool {
        matches!(
            self.merchant_account_type,
            common_enums::MerchantAccountType::Platform
        )
    }
}

#[cfg(feature = "v1")]
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
pub enum MerchantAccountUpdate {
    Update {
        merchant_name: OptionalEncryptableName,
        merchant_details: OptionalEncryptableValue,
        return_url: Option<String>,
        webhook_details: Option<diesel_models::business_profile::WebhookDetails>,
        sub_merchants_enabled: Option<bool>,
        parent_merchant_id: Option<common_utils::id_type::MerchantId>,
        enable_payment_response_hash: Option<bool>,
        payment_response_hash_key: Option<String>,
        redirect_to_merchant_with_http_post: Option<bool>,
        publishable_key: Option<String>,
        locker_id: Option<String>,
        metadata: Option<pii::SecretSerdeValue>,
        routing_algorithm: Option<serde_json::Value>,
        primary_business_details: Option<serde_json::Value>,
        intent_fulfillment_time: Option<i64>,
        frm_routing_algorithm: Option<serde_json::Value>,
        payout_routing_algorithm: Option<serde_json::Value>,
        default_profile: Option<Option<common_utils::id_type::ProfileId>>,
        payment_link_config: Option<serde_json::Value>,
        pm_collect_link_config: Option<serde_json::Value>,
    },
    StorageSchemeUpdate {
        storage_scheme: MerchantStorageScheme,
    },
    ReconUpdate {
        recon_status: diesel_models::enums::ReconStatus,
    },
    UnsetDefaultProfile,
    ModifiedAtUpdate,
    ToPlatformAccount,
}

#[cfg(feature = "v2")]
#[derive(Debug, Clone)]
pub enum MerchantAccountUpdate {
    Update {
        merchant_name: OptionalEncryptableName,
        merchant_details: OptionalEncryptableValue,
        publishable_key: Option<String>,
        metadata: Option<Box<pii::SecretSerdeValue>>,
    },
    StorageSchemeUpdate {
        storage_scheme: MerchantStorageScheme,
    },
    ReconUpdate {
        recon_status: diesel_models::enums::ReconStatus,
    },
    ModifiedAtUpdate,
    ToPlatformAccount,
}

#[cfg(feature = "v1")]
impl From<MerchantAccountUpdate> for MerchantAccountUpdateInternal {
    fn from(merchant_account_update: MerchantAccountUpdate) -> Self {
        let now = date_time::now();

        match merchant_account_update {
            MerchantAccountUpdate::Update {
                merchant_name,
                merchant_details,
                webhook_details,
                return_url,
                routing_algorithm,
                sub_merchants_enabled,
                parent_merchant_id,
                enable_payment_response_hash,
                payment_response_hash_key,
                redirect_to_merchant_with_http_post,
                publishable_key,
                locker_id,
                metadata,
                primary_business_details,
                intent_fulfillment_time,
                frm_routing_algorithm,
                payout_routing_algorithm,
                default_profile,
                payment_link_config,
                pm_collect_link_config,
            } => Self {
                merchant_name: merchant_name.map(Encryption::from),
                merchant_details: merchant_details.map(Encryption::from),
                frm_routing_algorithm,
                webhook_details,
                routing_algorithm,
                sub_merchants_enabled,
                parent_merchant_id,
                return_url,
                enable_payment_response_hash,
                payment_response_hash_key,
                redirect_to_merchant_with_http_post,
                publishable_key,
                locker_id,
                metadata,
                primary_business_details,
                modified_at: now,
                intent_fulfillment_time,
                payout_routing_algorithm,
                default_profile,
                payment_link_config,
                pm_collect_link_config,
                storage_scheme: None,
                organization_id: None,
                is_recon_enabled: None,
                recon_status: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self {
                storage_scheme: Some(storage_scheme),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                default_profile: None,
                recon_status: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ReconUpdate { recon_status } => Self {
                recon_status: Some(recon_status),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                storage_scheme: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                default_profile: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::UnsetDefaultProfile => Self {
                default_profile: Some(None),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                storage_scheme: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                recon_status: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ModifiedAtUpdate => Self {
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                storage_scheme: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                default_profile: None,
                recon_status: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ToPlatformAccount => Self {
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                return_url: None,
                webhook_details: None,
                sub_merchants_enabled: None,
                parent_merchant_id: None,
                enable_payment_response_hash: None,
                payment_response_hash_key: None,
                redirect_to_merchant_with_http_post: None,
                publishable_key: None,
                storage_scheme: None,
                locker_id: None,
                metadata: None,
                routing_algorithm: None,
                primary_business_details: None,
                intent_fulfillment_time: None,
                frm_routing_algorithm: None,
                payout_routing_algorithm: None,
                organization_id: None,
                is_recon_enabled: None,
                default_profile: None,
                recon_status: None,
                payment_link_config: None,
                pm_collect_link_config: None,
                is_platform_account: Some(true),
                product_type: None,
            },
        }
    }
}

#[cfg(feature = "v2")]
impl From<MerchantAccountUpdate> for MerchantAccountUpdateInternal {
    fn from(merchant_account_update: MerchantAccountUpdate) -> Self {
        let now = date_time::now();

        match merchant_account_update {
            MerchantAccountUpdate::Update {
                merchant_name,
                merchant_details,
                publishable_key,
                metadata,
            } => Self {
                merchant_name: merchant_name.map(Encryption::from),
                merchant_details: merchant_details.map(Encryption::from),
                publishable_key,
                metadata: metadata.map(|metadata| *metadata),
                modified_at: now,
                storage_scheme: None,
                organization_id: None,
                recon_status: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self {
                storage_scheme: Some(storage_scheme),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                publishable_key: None,
                metadata: None,
                organization_id: None,
                recon_status: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ReconUpdate { recon_status } => Self {
                recon_status: Some(recon_status),
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                publishable_key: None,
                storage_scheme: None,
                metadata: None,
                organization_id: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ModifiedAtUpdate => Self {
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                publishable_key: None,
                storage_scheme: None,
                metadata: None,
                organization_id: None,
                recon_status: None,
                is_platform_account: None,
                product_type: None,
            },
            MerchantAccountUpdate::ToPlatformAccount => Self {
                modified_at: now,
                merchant_name: None,
                merchant_details: None,
                publishable_key: None,
                storage_scheme: None,
                metadata: None,
                organization_id: None,
                recon_status: None,
                is_platform_account: Some(true),
                product_type: None,
            },
        }
    }
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl super::behaviour::Conversion for MerchantAccount {
    type DstType = diesel_models::merchant_account::MerchantAccount;
    type NewDstType = diesel_models::merchant_account::MerchantAccountNew;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        let id = self.get_id().to_owned();

        let setter = diesel_models::merchant_account::MerchantAccountSetter {
            id,
            merchant_name: self.merchant_name.map(|name| name.into()),
            merchant_details: self.merchant_details.map(|details| details.into()),
            publishable_key: Some(self.publishable_key),
            storage_scheme: self.storage_scheme,
            metadata: self.metadata,
            created_at: self.created_at,
            modified_at: self.modified_at,
            organization_id: self.organization_id,
            recon_status: self.recon_status,
            version: common_types::consts::API_VERSION,
            is_platform_account: self.is_platform_account,
            product_type: self.product_type,
            merchant_account_type: self.merchant_account_type,
        };

        Ok(diesel_models::MerchantAccount::from(setter))
    }

    async fn convert_back(
        state: &keymanager::KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        let id = item.get_id().to_owned();
        let publishable_key =
            item.publishable_key
                .ok_or(ValidationError::MissingRequiredField {
                    field_name: "publishable_key".to_string(),
                })?;

        async {
            Ok::<Self, error_stack::Report<common_utils::errors::CryptoError>>(Self {
                id,
                merchant_name: item
                    .merchant_name
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                merchant_details: item
                    .merchant_details
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                publishable_key,
                storage_scheme: item.storage_scheme,
                metadata: item.metadata,
                created_at: item.created_at,
                modified_at: item.modified_at,
                organization_id: item.organization_id,
                recon_status: item.recon_status,
                is_platform_account: item.is_platform_account,
                version: item.version,
                product_type: item.product_type,
                merchant_account_type: item.merchant_account_type.unwrap_or_default(),
            })
        }
        .await
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting merchant data".to_string(),
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        let now = date_time::now();
        Ok(diesel_models::merchant_account::MerchantAccountNew {
            id: self.id,
            merchant_name: self.merchant_name.map(Encryption::from),
            merchant_details: self.merchant_details.map(Encryption::from),
            publishable_key: Some(self.publishable_key),
            metadata: self.metadata,
            created_at: now,
            modified_at: now,
            organization_id: self.organization_id,
            recon_status: self.recon_status,
            version: common_types::consts::API_VERSION,
            is_platform_account: self.is_platform_account,
            product_type: self
                .product_type
                .or(Some(common_enums::MerchantProductType::Orchestration)),
            merchant_account_type: self.merchant_account_type,
        })
    }
}

#[cfg(feature = "v1")]
#[async_trait::async_trait]
impl super::behaviour::Conversion for MerchantAccount {
    type DstType = diesel_models::merchant_account::MerchantAccount;
    type NewDstType = diesel_models::merchant_account::MerchantAccountNew;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        let setter = diesel_models::merchant_account::MerchantAccountSetter {
            merchant_id: self.merchant_id,
            return_url: self.return_url,
            enable_payment_response_hash: self.enable_payment_response_hash,
            payment_response_hash_key: self.payment_response_hash_key,
            redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post,
            merchant_name: self.merchant_name.map(|name| name.into()),
            merchant_details: self.merchant_details.map(|details| details.into()),
            webhook_details: self.webhook_details,
            sub_merchants_enabled: self.sub_merchants_enabled,
            parent_merchant_id: self.parent_merchant_id,
            publishable_key: Some(self.publishable_key),
            storage_scheme: self.storage_scheme,
            locker_id: self.locker_id,
            metadata: self.metadata,
            routing_algorithm: self.routing_algorithm,
            primary_business_details: self.primary_business_details,
            created_at: self.created_at,
            modified_at: self.modified_at,
            intent_fulfillment_time: self.intent_fulfillment_time,
            frm_routing_algorithm: self.frm_routing_algorithm,
            payout_routing_algorithm: self.payout_routing_algorithm,
            organization_id: self.organization_id,
            is_recon_enabled: self.is_recon_enabled,
            default_profile: self.default_profile,
            recon_status: self.recon_status,
            payment_link_config: self.payment_link_config,
            pm_collect_link_config: self.pm_collect_link_config,
            version: self.version,
            is_platform_account: self.is_platform_account,
            product_type: self.product_type,
            merchant_account_type: self.merchant_account_type,
        };

        Ok(diesel_models::MerchantAccount::from(setter))
    }

    async fn convert_back(
        state: &keymanager::KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        let merchant_id = item.get_id().to_owned();
        let publishable_key =
            item.publishable_key
                .ok_or(ValidationError::MissingRequiredField {
                    field_name: "publishable_key".to_string(),
                })?;

        async {
            Ok::<Self, error_stack::Report<common_utils::errors::CryptoError>>(Self {
                merchant_id,
                return_url: item.return_url,
                enable_payment_response_hash: item.enable_payment_response_hash,
                payment_response_hash_key: item.payment_response_hash_key,
                redirect_to_merchant_with_http_post: item.redirect_to_merchant_with_http_post,
                merchant_name: item
                    .merchant_name
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                merchant_details: item
                    .merchant_details
                    .async_lift(|inner| async {
                        crypto_operation(
                            state,
                            type_name!(Self::DstType),
                            CryptoOperation::DecryptOptional(inner),
                            key_manager_identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_optionaloperation())
                    })
                    .await?,
                webhook_details: item.webhook_details,
                sub_merchants_enabled: item.sub_merchants_enabled,
                parent_merchant_id: item.parent_merchant_id,
                publishable_key,
                storage_scheme: item.storage_scheme,
                locker_id: item.locker_id,
                metadata: item.metadata,
                routing_algorithm: item.routing_algorithm,
                frm_routing_algorithm: item.frm_routing_algorithm,
                primary_business_details: item.primary_business_details,
                created_at: item.created_at,
                modified_at: item.modified_at,
                intent_fulfillment_time: item.intent_fulfillment_time,
                payout_routing_algorithm: item.payout_routing_algorithm,
                organization_id: item.organization_id,
                is_recon_enabled: item.is_recon_enabled,
                default_profile: item.default_profile,
                recon_status: item.recon_status,
                payment_link_config: item.payment_link_config,
                pm_collect_link_config: item.pm_collect_link_config,
                version: item.version,
                is_platform_account: item.is_platform_account,
                product_type: item.product_type,
                merchant_account_type: item.merchant_account_type.unwrap_or_default(),
            })
        }
        .await
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting merchant data".to_string(),
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        let now = date_time::now();
        Ok(diesel_models::merchant_account::MerchantAccountNew {
            id: Some(self.merchant_id.clone()),
            merchant_id: self.merchant_id,
            merchant_name: self.merchant_name.map(Encryption::from),
            merchant_details: self.merchant_details.map(Encryption::from),
            return_url: self.return_url,
            webhook_details: self.webhook_details,
            sub_merchants_enabled: self.sub_merchants_enabled,
            parent_merchant_id: self.parent_merchant_id,
            enable_payment_response_hash: Some(self.enable_payment_response_hash),
            payment_response_hash_key: self.payment_response_hash_key,
            redirect_to_merchant_with_http_post: Some(self.redirect_to_merchant_with_http_post),
            publishable_key: Some(self.publishable_key),
            locker_id: self.locker_id,
            metadata: self.metadata,
            routing_algorithm: self.routing_algorithm,
            primary_business_details: self.primary_business_details,
            created_at: now,
            modified_at: now,
            intent_fulfillment_time: self.intent_fulfillment_time,
            frm_routing_algorithm: self.frm_routing_algorithm,
            payout_routing_algorithm: self.payout_routing_algorithm,
            organization_id: self.organization_id,
            is_recon_enabled: self.is_recon_enabled,
            default_profile: self.default_profile,
            recon_status: self.recon_status,
            payment_link_config: self.payment_link_config,
            pm_collect_link_config: self.pm_collect_link_config,
            version: common_types::consts::API_VERSION,
            is_platform_account: self.is_platform_account,
            product_type: self
                .product_type
                .or(Some(common_enums::MerchantProductType::Orchestration)),
            merchant_account_type: self.merchant_account_type,
        })
    }
}

impl MerchantAccount {
    pub fn get_compatible_connector(&self) -> Option<api_models::enums::Connector> {
        let metadata: Option<api_models::admin::MerchantAccountMetadata> =
            self.metadata.as_ref().and_then(|meta| {
                meta.clone()
                    .parse_value("MerchantAccountMetadata")
                    .map_err(|err| logger::error!("Failed to deserialize {:?}", err))
                    .ok()
            });
        metadata.and_then(|a| a.compatible_connector)
    }
}


// Function: get_default_customer_billing_address
// File: crates/api_models/src/customers.rs
// Module: api_models

pub fn get_default_customer_billing_address(&self) -> Option<payments::AddressDetails>