            #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
            business_profile,
        ))
        .await?;

        Ok(payment_data)
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsRejectData> for PaymentResponse {
    async fn update_tracker<'b>(
        &'b self,
        db: &'b SessionState,
        mut payment_data: PaymentData<F>,
        router_data: types::RouterData<F, types::PaymentsRejectData, types::PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
        locale: &Option<String>,
        #[cfg(all(feature = "v1", feature = "dynamic_routing"))] routable_connector: Vec<
            RoutableConnectorChoice,
        >,
        #[cfg(all(feature = "v1", feature = "dynamic_routing"))] business_profile: &domain::Profile,
    ) -> RouterResult<PaymentData<F>>
    where
        F: 'b + Send,
    {
        payment_data = Box::pin(payment_response_update_tracker(
            db,
            payment_data,
            router_data,
            key_store,
            storage_scheme,
            locale,
            #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
            routable_connector,
            #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
            business_profile,
        ))
        .await?;

        Ok(payment_data)
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::SetupMandateRequestData>
    for PaymentResponse
{
    async fn update_tracker<'b>(
        &'b self,
        db: &'b SessionState,
        mut payment_data: PaymentData<F>,
        router_data: types::RouterData<
            F,
            types::SetupMandateRequestData,
            types::PaymentsResponseData,
        >,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
        locale: &Option<String>,
        #[cfg(all(feature = "v1", feature = "dynamic_routing"))] routable_connector: Vec<
            RoutableConnectorChoice,
        >,
        #[cfg(all(feature = "v1", feature = "dynamic_routing"))] business_profile: &domain::Profile,
    ) -> RouterResult<PaymentData<F>>
    where
        F: 'b + Send,
    {
        payment_data.mandate_id = payment_data.mandate_id.or_else(|| {
            router_data.request.mandate_id.clone()
            // .map(api_models::payments::MandateIds::new)
        });

        payment_data = Box::pin(payment_response_update_tracker(
            db,
            payment_data,
            router_data,
            key_store,
            storage_scheme,
            locale,
            #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
            routable_connector,
            #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
            business_profile,
        ))
        .await?;

        Ok(payment_data)
    }

    async fn save_pm_and_mandate<'b>(
        &self,
        state: &SessionState,
        resp: &types::RouterData<F, types::SetupMandateRequestData, types::PaymentsResponseData>,
        merchant_context: &domain::MerchantContext,
        payment_data: &mut PaymentData<F>,
        business_profile: &domain::Profile,
    ) -> CustomResult<(), errors::ApiErrorResponse>
    where
        F: 'b + Clone + Send + Sync,
    {
        let payment_method_billing_address = payment_data.address.get_payment_method_billing();
        let billing_name = resp
            .address
            .get_payment_method_billing()
            .and_then(|billing_details| billing_details.address.as_ref())
            .and_then(|address| address.get_optional_full_name());

        let save_payment_data = tokenization::SavePaymentMethodData::from(resp);
        let customer_id = payment_data.payment_intent.customer_id.clone();
        let connector_name = payment_data
            .payment_attempt
            .connector
            .clone()
            .ok_or_else(|| {
                logger::error!("Missing required Param connector_name");
                errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "connector_name",
                }
            })?;
        let connector_mandate_reference_id = payment_data
            .payment_attempt
            .connector_mandate_detail
            .as_ref()
            .map(|detail| ConnectorMandateReferenceId::foreign_from(detail.clone()));
        let vault_operation = payment_data.vault_operation.clone();
        let payment_method_info = payment_data.payment_method_info.clone();
        let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone();
        let tokenization::SavePaymentMethodDataResponse {
            payment_method_id,
            connector_mandate_reference_id,
            ..
        } = Box::pin(tokenization::save_payment_method(
            state,
            connector_name,
            save_payment_data,
            customer_id.clone(),
            merchant_context,
            resp.request.payment_method_type,
            billing_name,
            payment_method_billing_address,
            business_profile,
            connector_mandate_reference_id,
            merchant_connector_id.clone(),
            vault_operation,
            payment_method_info,
        ))
        .await?;

        payment_data.payment_method_info = if let Some(payment_method_id) = &payment_method_id {
            match state
                .store
                .find_payment_method(
                    &(state.into()),
                    merchant_context.get_merchant_key_store(),
                    payment_method_id,
                    merchant_context.get_merchant_account().storage_scheme,
                )
                .await
            {
                Ok(payment_method) => Some(payment_method),
                Err(error) => {
                    if error.current_context().is_db_not_found() {
                        logger::info!("Payment Method not found in db {:?}", error);
                        None
                    } else {
                        Err(error)
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Error retrieving payment method from db")
                            .map_err(|err| logger::error!(payment_method_retrieve=?err))
                            .ok()
                    }
                }
            }
        } else {
            None
        };
        let mandate_id = mandate::mandate_procedure(
            state,
            resp,
            &customer_id,
            payment_method_id.clone(),
            merchant_connector_id.clone(),
            merchant_context.get_merchant_account().storage_scheme,
            payment_data.payment_intent.get_id(),
        )
        .await?;
        payment_data.payment_attempt.payment_method_id = payment_method_id;
        payment_data.payment_attempt.mandate_id = mandate_id;
        payment_data.payment_attempt.connector_mandate_detail = connector_mandate_reference_id
            .clone()
            .map(ForeignFrom::foreign_from);
        payment_data.set_mandate_id(api_models::payments::MandateIds {
            mandate_id: None,
            mandate_reference_id: connector_mandate_reference_id.map(|connector_mandate_id| {
                MandateReferenceId::ConnectorMandateId(connector_mandate_id)
            }),
        });
        Ok(())
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::CompleteAuthorizeData>
    for PaymentResponse
{
    async fn update_tracker<'b>(
        &'b self,
        db: &'b SessionState,
        payment_data: PaymentData<F>,
        response: types::RouterData<F, types::CompleteAuthorizeData, types::PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
        locale: &Option<String>,
        #[cfg(all(feature = "v1", feature = "dynamic_routing"))] routable_connector: Vec<
            RoutableConnectorChoice,
        >,
        #[cfg(all(feature = "v1", feature = "dynamic_routing"))] business_profile: &domain::Profile,
    ) -> RouterResult<PaymentData<F>>
    where
        F: 'b + Send,
    {
        Box::pin(payment_response_update_tracker(
            db,
            payment_data,
            response,
            key_store,
            storage_scheme,
            locale,
            #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
            routable_connector,
            #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
            business_profile,
        ))
        .await
    }

    async fn save_pm_and_mandate<'b>(
        &self,
        state: &SessionState,
        resp: &types::RouterData<F, types::CompleteAuthorizeData, types::PaymentsResponseData>,
        merchant_context: &domain::MerchantContext,
        payment_data: &mut PaymentData<F>,
        _business_profile: &domain::Profile,
    ) -> CustomResult<(), errors::ApiErrorResponse>
    where
        F: 'b + Clone + Send + Sync,
    {
        let (connector_mandate_id, mandate_metadata, connector_mandate_request_reference_id) = resp
            .response
            .clone()
            .ok()
            .and_then(|resp| {
                if let types::PaymentsResponseData::TransactionResponse {
                    mandate_reference, ..
                } = resp
                {
                    mandate_reference.map(|mandate_ref| {
                        (
                            mandate_ref.connector_mandate_id.clone(),
                            mandate_ref.mandate_metadata.clone(),
                            mandate_ref.connector_mandate_request_reference_id.clone(),
                        )
                    })
                } else {
                    None
                }
            })
            .unwrap_or((None, None, None));
        update_connector_mandate_details_for_the_flow(
            connector_mandate_id,
            mandate_metadata,
            connector_mandate_request_reference_id,
            payment_data,
        )?;

        update_payment_method_status_and_ntid(
            state,
            merchant_context.get_merchant_key_store(),
            payment_data,
            resp.status,
            resp.response.clone(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;
        Ok(())
    }
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
    state: &SessionState,
    mut payment_data: PaymentData<F>,
    router_data: types::RouterData<F, T, types::PaymentsResponseData>,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: enums::MerchantStorageScheme,
    locale: &Option<String>,
    #[cfg(all(feature = "v1", feature = "dynamic_routing"))] routable_connectors: Vec<
        RoutableConnectorChoice,
    >,
    #[cfg(all(feature = "v1", feature = "dynamic_routing"))] business_profile: &domain::Profile,
) -> RouterResult<PaymentData<F>> {
    // Update additional payment data with the payment method response that we received from connector
    // This is for details like whether 3ds was upgraded and which version of 3ds was used
    // also some connectors might send card network details in the response, which is captured and stored

    let additional_payment_method_data = match payment_data.payment_method_data.clone() {
        Some(payment_method_data) => match payment_method_data {
            hyperswitch_domain_models::payment_method_data::PaymentMethodData::Card(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardRedirect(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Wallet(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::PayLater(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankRedirect(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankDebit(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankTransfer(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Crypto(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::MandatePayment
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Reward
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::RealTimePayment(
                _,
            )
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::MobilePayment(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Upi(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Voucher(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::GiftCard(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardToken(_)
            | hyperswitch_domain_models::payment_method_data::PaymentMethodData::OpenBanking(_) => {
                update_additional_payment_data_with_connector_response_pm_data(
                    payment_data.payment_attempt.payment_method_data.clone(),
                    router_data
                        .connector_response
                        .as_ref()
                        .and_then(|connector_response| {
                            connector_response.additional_payment_method_data.clone()
                        }),
                )?
            }
            hyperswitch_domain_models::payment_method_data::PaymentMethodData::NetworkToken(_) => {
                payment_data.payment_attempt.payment_method_data.clone()
            }
            hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                payment_data.payment_attempt.payment_method_data.clone()
            }
        },
        None => None,
    };

    router_data.payment_method_status.and_then(|status| {
        payment_data
            .payment_method_info
            .as_mut()
            .map(|info| info.status = status)
    });
    payment_data.whole_connector_response = router_data.raw_connector_response.clone();

    // TODO: refactor of gsm_error_category with respective feature flag
    #[allow(unused_variables)]
    let (capture_update, mut payment_attempt_update, gsm_error_category) = match router_data
        .response
        .clone()
    {
        Err(err) => {
            let auth_update = if Some(router_data.auth_type)
                != payment_data.payment_attempt.authentication_type
            {
                Some(router_data.auth_type)
            } else {
                None
            };
            let (capture_update, attempt_update, gsm_error_category) =
                match payment_data.multiple_capture_data {
                    Some(multiple_capture_data) => {
                        let capture_update = storage::CaptureUpdate::ErrorUpdate {
                            status: match err.status_code {
                                500..=511 => enums::CaptureStatus::Pending,
                                _ => enums::CaptureStatus::Failed,
                            },
                            error_code: Some(err.code),
                            error_message: Some(err.message),
                            error_reason: err.reason,
                        };
                        let capture_update_list = vec![(
                            multiple_capture_data.get_latest_capture().clone(),
                            capture_update,
                        )];
                        (
                            Some((multiple_capture_data, capture_update_list)),
                            auth_update.map(|auth_type| {
                                storage::PaymentAttemptUpdate::AuthenticationTypeUpdate {
                                    authentication_type: auth_type,
                                    updated_by: storage_scheme.to_string(),
                                }
                            }),
                            None,
                        )
                    }
                    None => {
                        let connector_name = router_data.connector.to_string();
                        let flow_name = core_utils::get_flow_name::<F>()?;
                        let option_gsm = payments_helpers::get_gsm_record(
                            state,
                            Some(err.code.clone()),
                            Some(err.message.clone()),
                            connector_name,
                            flow_name.clone(),
                        )
                        .await;

                        let gsm_unified_code =
                            option_gsm.as_ref().and_then(|gsm| gsm.unified_code.clone());
                        let gsm_unified_message =
                            option_gsm.clone().and_then(|gsm| gsm.unified_message);

                        let (unified_code, unified_message) = if let Some((code, message)) =
                            gsm_unified_code.as_ref().zip(gsm_unified_message.as_ref())
                        {
                            (code.to_owned(), message.to_owned())
                        } else {
                            (
                                consts::DEFAULT_UNIFIED_ERROR_CODE.to_owned(),
                                consts::DEFAULT_UNIFIED_ERROR_MESSAGE.to_owned(),
                            )
                        };
                        let unified_translated_message = locale
                            .as_ref()
                            .async_and_then(|locale_str| async {
                                payments_helpers::get_unified_translation(
                                    state,
                                    unified_code.to_owned(),
                                    unified_message.to_owned(),
                                    locale_str.to_owned(),
                                )
                                .await
                            })
                            .await
                            .or(Some(unified_message));

                        let status = match err.attempt_status {
                            // Use the status sent by connector in error_response if it's present
                            Some(status) => status,
                            None =>
                            // mark previous attempt status for technical failures in PSync flow
                            {
                                if flow_name == "PSync" {
                                    match err.status_code {
                                        // marking failure for 2xx because this is genuine payment failure
                                        200..=299 => enums::AttemptStatus::Failure,
                                        _ => router_data.status,
                                    }
                                } else if flow_name == "Capture" {
                                    match err.status_code {
                                        500..=511 => enums::AttemptStatus::Pending,
                                        // don't update the status for 429 error status
                                        429 => router_data.status,
                                        _ => enums::AttemptStatus::Failure,
                                    }
                                } else {
                                    match err.status_code {
                                        500..=511 => enums::AttemptStatus::Pending,
                                        _ => enums::AttemptStatus::Failure,
                                    }
                                }
                            }
                        };
                        (
                            None,
                            Some(storage::PaymentAttemptUpdate::ErrorUpdate {
                                connector: None,
                                status,
                                error_message: Some(Some(err.message)),
                                error_code: Some(Some(err.code)),
                                error_reason: Some(err.reason),
                                amount_capturable: router_data
                                    .request
                                    .get_amount_capturable(
                                        &payment_data,
                                        router_data
                                            .minor_amount_capturable
                                            .map(MinorUnit::get_amount_as_i64),
                                        status,
                                    )
                                    .map(MinorUnit::new),
                                updated_by: storage_scheme.to_string(),
                                unified_code: Some(Some(unified_code)),
                                unified_message: Some(unified_translated_message),
                                connector_transaction_id: err.connector_transaction_id,
                                payment_method_data: additional_payment_method_data,
                                authentication_type: auth_update,
                                issuer_error_code: err.network_decline_code,
                                issuer_error_message: err.network_error_message,
                            }),
                            option_gsm.and_then(|option_gsm| option_gsm.error_category),
                        )
                    }
                };
            (capture_update, attempt_update, gsm_error_category)
        }

        Ok(payments_response) => {
            // match on connector integrity check
            match router_data.integrity_check.clone() {
                Err(err) => {
                    let auth_update = if Some(router_data.auth_type)
                        != payment_data.payment_attempt.authentication_type
                    {
                        Some(router_data.auth_type)
                    } else {
                        None
                    };
                    let field_name = err.field_names;
                    let connector_transaction_id = err.connector_transaction_id;
                    (
                        None,
                        Some(storage::PaymentAttemptUpdate::ErrorUpdate {
                            connector: None,
                            status: enums::AttemptStatus::IntegrityFailure,
                            error_message: Some(Some("Integrity Check Failed!".to_string())),
                            error_code: Some(Some("IE".to_string())),
                            error_reason: Some(Some(format!(
                                "Integrity Check Failed! Value mismatched for fields {field_name}"
                            ))),
                            amount_capturable: None,
                            updated_by: storage_scheme.to_string(),
                            unified_code: None,
                            unified_message: None,
                            connector_transaction_id,
                            payment_method_data: None,
                            authentication_type: auth_update,
                            issuer_error_code: None,
                            issuer_error_message: None,
                        }),
                        None,
                    )
                }
                Ok(()) => {
                    let attempt_status = payment_data.payment_attempt.status.to_owned();
                    let connector_status = router_data.status.to_owned();
                    let updated_attempt_status = match (
                        connector_status,
                        attempt_status,
                        payment_data.frm_message.to_owned(),
                    ) {
                        (
                            enums::AttemptStatus::Authorized,
                            enums::AttemptStatus::Unresolved,
                            Some(frm_message),
                        ) => match frm_message.frm_status {
                            enums::FraudCheckStatus::Fraud
                            | enums::FraudCheckStatus::ManualReview => attempt_status,
                            _ => router_data.get_attempt_status_for_db_update(
                                &payment_data,
                                router_data.amount_captured,
                                router_data
                                    .minor_amount_capturable
                                    .map(MinorUnit::get_amount_as_i64),
                            )?,
                        },
                        _ => router_data.get_attempt_status_for_db_update(
                            &payment_data,
                            router_data.amount_captured,
                            router_data
                                .minor_amount_capturable
                                .map(MinorUnit::get_amount_as_i64),
                        )?,
                    };
                    match payments_response {
                        types::PaymentsResponseData::PreProcessingResponse {
                            pre_processing_id,
                            connector_metadata,
                            connector_response_reference_id,
                            ..
                        } => {
                            let connector_transaction_id = match pre_processing_id.to_owned() {
                                types::PreprocessingResponseId::PreProcessingId(_) => None,

                                types::PreprocessingResponseId::ConnectorTransactionId(
                                    connector_txn_id,
                                ) => Some(connector_txn_id),
                            };
                            let preprocessing_step_id = match pre_processing_id {
                                types::PreprocessingResponseId::PreProcessingId(
                                    pre_processing_id,
                                ) => Some(pre_processing_id),
                                types::PreprocessingResponseId::ConnectorTransactionId(_) => None,
                            };
                            let payment_attempt_update =
                                storage::PaymentAttemptUpdate::PreprocessingUpdate {
                                    status: updated_attempt_status,
                                    payment_method_id: payment_data
                                        .payment_attempt
                                        .payment_method_id
                                        .clone(),
                                    connector_metadata,
                                    preprocessing_step_id,
                                    connector_transaction_id,
                                    connector_response_reference_id,
                                    updated_by: storage_scheme.to_string(),
                                };

                            (None, Some(payment_attempt_update), None)
                        }
                        types::PaymentsResponseData::TransactionResponse {
                            resource_id,
                            redirection_data,
                            connector_metadata,
                            connector_response_reference_id,
                            incremental_authorization_allowed,
                            charges,
                            ..
                        } => {
                            payment_data
                                .payment_intent
                                .incremental_authorization_allowed =
                                core_utils::get_incremental_authorization_allowed_value(
                                    incremental_authorization_allowed,
                                    payment_data
                                        .payment_intent
                                        .request_incremental_authorization,
                                );
                            let connector_transaction_id = match resource_id {
                                types::ResponseId::NoResponseId => None,
                                types::ResponseId::ConnectorTransactionId(ref id)
                                | types::ResponseId::EncodedData(ref id) => Some(id),
                            };
                            let resp_network_transaction_id = router_data.response.as_ref()
                                .map_err(|err| {
                                    logger::error!(error = ?err, "Failed to obtain the network_transaction_id from payment response");
                                })
                                .ok()
                                .and_then(|resp| resp.get_network_transaction_id());

                            let encoded_data = payment_data.payment_attempt.encoded_data.clone();

                            let authentication_data = (*redirection_data)
                                .as_ref()
                                .map(Encode::encode_to_value)
                                .transpose()
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable("Could not parse the connector response")?;

                            let auth_update = if Some(router_data.auth_type)
                                != payment_data.payment_attempt.authentication_type
                            {
                                Some(router_data.auth_type)
                            } else {
                                None
                            };

                            // incase of success, update error code and error message
                            let error_status =
                                if router_data.status == enums::AttemptStatus::Charged {
                                    Some(None)
                                } else {
                                    None
                                };
                            // update connector_mandate_details in case of Authorized/Charged Payment Status
                            if matches!(
                                router_data.status,
                                enums::AttemptStatus::Charged
                                    | enums::AttemptStatus::Authorized
                                    | enums::AttemptStatus::PartiallyAuthorized
                            ) {
                                payment_data
                                    .payment_intent
                                    .fingerprint_id
                                    .clone_from(&payment_data.payment_attempt.fingerprint_id);

                                if let Some(payment_method) =
                                    payment_data.payment_method_info.clone()
                                {
                                    // Parse value to check for mandates' existence
                                    let mandate_details = payment_method
                                        .get_common_mandate_reference()
                                        .change_context(
                                            errors::ApiErrorResponse::InternalServerError,
                                        )
                                        .attach_printable(
                                            "Failed to deserialize to Payment Mandate Reference ",
                                        )?;

                                    if let Some(mca_id) =
                                        payment_data.payment_attempt.merchant_connector_id.clone()
                                    {
                                        // check if the mandate has not already been set to active
                                        if !mandate_details.payments
                                            .as_ref()
                                            .and_then(|payments| payments.0.get(&mca_id))
                                                    .map(|payment_mandate_reference_record| payment_mandate_reference_record.connector_mandate_status == Some(common_enums::ConnectorMandateStatus::Active))
                                                    .unwrap_or(false)
                                    {

                                        let (connector_mandate_id, mandate_metadata,connector_mandate_request_reference_id) = payment_data.payment_attempt.connector_mandate_detail.clone()
                                        .map(|cmr| (cmr.connector_mandate_id, cmr.mandate_metadata,cmr.connector_mandate_request_reference_id))
                                        .unwrap_or((None, None,None));
                                        // Update the connector mandate details with the payment attempt connector mandate id
                                        let connector_mandate_details =
                                                    tokenization::update_connector_mandate_details(
                                                        Some(mandate_details),
                                                        payment_data.payment_attempt.payment_method_type,
                                                        Some(
                                                            payment_data
                                                                .payment_attempt
                                                                .net_amount
                                                                .get_total_amount()
                                                                .get_amount_as_i64(),
                                                        ),
                                                        payment_data.payment_attempt.currency,
                                                        payment_data.payment_attempt.merchant_connector_id.clone(),
                                                        connector_mandate_id,
                                                        mandate_metadata,
                                                        connector_mandate_request_reference_id
                                                    )?;
                                        // Update the payment method table with the active mandate record
                                        payment_methods::cards::update_payment_method_connector_mandate_details(
                                                        state,
                                                        key_store,
                                                        &*state.store,
                                                        payment_method,
                                                        connector_mandate_details,
                                                        storage_scheme,
                                                    )
                                                    .await
                                                    .change_context(errors::ApiErrorResponse::InternalServerError)
                                                    .attach_printable("Failed to update payment method in db")?;
                                    }
                                    }
                                }

                                metrics::SUCCESSFUL_PAYMENT.add(1, &[]);
                            }

                            let payment_method_id =
                                payment_data.payment_attempt.payment_method_id.clone();

                            let debit_routing_savings =
                                payment_data.payment_method_data.as_ref().and_then(|data| {
                                    payments_helpers::get_debit_routing_savings_amount(
                                        data,
                                        &payment_data.payment_attempt,
                                    )
                                });

                            utils::add_apple_pay_payment_status_metrics(
                                router_data.status,
                                router_data.apple_pay_flow.clone(),
                                payment_data.payment_attempt.connector.clone(),
                                payment_data.payment_attempt.merchant_id.clone(),
                            );
                            let (capture_before, extended_authorization_applied) = router_data
                                .connector_response
                                .as_ref()
                                .and_then(|connector_response| {
                                    connector_response.get_extended_authorization_response_data()
                                })
                                .map(|extended_auth_resp| {
                                    (
                                        extended_auth_resp.capture_before,
                                        extended_auth_resp.extended_authentication_applied,
                                    )
                                })
                                .unwrap_or((None, None));
                            let (capture_updates, payment_attempt_update) = match payment_data
                                .multiple_capture_data
                            {
                                Some(multiple_capture_data) => {
                                    let (connector_capture_id, processor_capture_data) =
                                        match resource_id {
                                            types::ResponseId::NoResponseId => (None, None),
                                            types::ResponseId::ConnectorTransactionId(id)
                                            | types::ResponseId::EncodedData(id) => {
                                                let (txn_id, txn_data) =
                                                    ConnectorTransactionId::form_id_and_data(id);
                                                (Some(txn_id), txn_data)
                                            }
                                        };
                                    let capture_update = storage::CaptureUpdate::ResponseUpdate {
                                        status: enums::CaptureStatus::foreign_try_from(
                                            router_data.status,
                                        )?,
                                        connector_capture_id: connector_capture_id.clone(),
                                        connector_response_reference_id,
                                        processor_capture_data: processor_capture_data.clone(),
                                    };
                                    let capture_update_list = vec![(
                                        multiple_capture_data.get_latest_capture().clone(),
                                        capture_update,
                                    )];
                                    (Some((multiple_capture_data, capture_update_list)), auth_update.map(|auth_type| {
                                        storage::PaymentAttemptUpdate::AuthenticationTypeUpdate {
                                            authentication_type: auth_type,
                                            updated_by: storage_scheme.to_string(),
                                        }
                                    }))
                                }
                                None => (
                                    None,
                                    Some(storage::PaymentAttemptUpdate::ResponseUpdate {
                                        status: updated_attempt_status,
                                        connector: None,
                                        connector_transaction_id: connector_transaction_id.cloned(),
                                        authentication_type: auth_update,
                                        amount_capturable: router_data
                                            .request
                                            .get_amount_capturable(
                                                &payment_data,
                                                router_data
                                                    .minor_amount_capturable
                                                    .map(MinorUnit::get_amount_as_i64),
                                                updated_attempt_status,
                                            )
                                            .map(MinorUnit::new),
                                        payment_method_id,
                                        mandate_id: payment_data.payment_attempt.mandate_id.clone(),
                                        connector_metadata,
                                        payment_token: None,
                                        error_code: error_status.clone(),
                                        error_message: error_status.clone(),
                                        error_reason: error_status.clone(),
                                        unified_code: error_status.clone(),
                                        unified_message: error_status,
                                        connector_response_reference_id,
                                        updated_by: storage_scheme.to_string(),
                                        authentication_data,
                                        encoded_data,
                                        payment_method_data: additional_payment_method_data,
                                        capture_before,
                                        extended_authorization_applied,
                                        connector_mandate_detail: payment_data
                                            .payment_attempt
                                            .connector_mandate_detail
                                            .clone(),
                                        charges,
                                        setup_future_usage_applied: payment_data
                                            .payment_attempt
                                            .setup_future_usage_applied,
                                        debit_routing_savings,
                                        network_transaction_id: resp_network_transaction_id,
                                    }),
                                ),
                            };

                            (capture_updates, payment_attempt_update, None)
                        }
                        types::PaymentsResponseData::TransactionUnresolvedResponse {
                            resource_id,
                            reason,
                            connector_response_reference_id,
                        } => {
                            let connector_transaction_id = match resource_id {
                                types::ResponseId::NoResponseId => None,
                                types::ResponseId::ConnectorTransactionId(id)
                                | types::ResponseId::EncodedData(id) => Some(id),
                            };
                            (
                                None,
                                Some(storage::PaymentAttemptUpdate::UnresolvedResponseUpdate {
                                    status: updated_attempt_status,
                                    connector: None,
                                    connector_transaction_id,
                                    payment_method_id: payment_data
                                        .payment_attempt
                                        .payment_method_id
                                        .clone(),
                                    error_code: Some(reason.clone().map(|cd| cd.code)),
                                    error_message: Some(reason.clone().map(|cd| cd.message)),
                                    error_reason: Some(reason.map(|cd| cd.message)),
                                    connector_response_reference_id,
                                    updated_by: storage_scheme.to_string(),
                                }),
                                None,
                            )
                        }
                        types::PaymentsResponseData::SessionResponse { .. } => (None, None, None),
                        types::PaymentsResponseData::SessionTokenResponse { .. } => {
                            (None, None, None)
                        }
                        types::PaymentsResponseData::TokenizationResponse { .. } => {
                            (None, None, None)
                        }
                        types::PaymentsResponseData::ConnectorCustomerResponse { .. } => {
                            (None, None, None)
                        }
                        types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => {
                            (None, None, None)
                        }
                        types::PaymentsResponseData::PostProcessingResponse { .. } => {
                            (None, None, None)
                        }
                        types::PaymentsResponseData::IncrementalAuthorizationResponse {
                            ..
                        } => (None, None, None),
                        types::PaymentsResponseData::PaymentResourceUpdateResponse { .. } => {
                            (None, None, None)
                        }
                        types::PaymentsResponseData::MultipleCaptureResponse {
                            capture_sync_response_list,
                        } => match payment_data.multiple_capture_data {
                            Some(multiple_capture_data) => {
                                let capture_update_list = response_to_capture_update(
                                    &multiple_capture_data,
                                    capture_sync_response_list,
                                )?;
                                (
                                    Some((multiple_capture_data, capture_update_list)),
                                    None,
                                    None,
                                )
                            }
                            None => (None, None, None),
                        },
                        types::PaymentsResponseData::PaymentsCreateOrderResponse { .. } => {
                            (None, None, None)
                        }
                    }
                }
            }
        }
    };
    payment_data.multiple_capture_data = match capture_update {
        Some((mut multiple_capture_data, capture_updates)) => {
            for (capture, capture_update) in capture_updates {
                let updated_capture = state
                    .store
                    .update_capture_with_capture_id(capture, capture_update, storage_scheme)
                    .await
                    .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
                multiple_capture_data.update_capture(updated_capture);
            }

            let authorized_amount = payment_data.payment_attempt.get_total_amount();

            payment_attempt_update = Some(storage::PaymentAttemptUpdate::AmountToCaptureUpdate {
                status: multiple_capture_data.get_attempt_status(authorized_amount),
                amount_capturable: authorized_amount
                    - multiple_capture_data.get_total_blocked_amount(),
                updated_by: storage_scheme.to_string(),
            });
            Some(multiple_capture_data)
        }
        None => None,
    };

    // Stage 1

    let payment_attempt = payment_data.payment_attempt.clone();

    let m_db = state.clone().store;
    let m_payment_attempt_update = payment_attempt_update.clone();
    let m_payment_attempt = payment_attempt.clone();

    let payment_attempt = payment_attempt_update
        .map(|payment_attempt_update| {
            PaymentAttempt::from_storage_model(
                payment_attempt_update
                    .to_storage_model()
                    .apply_changeset(payment_attempt.clone().to_storage_model()),
            )
        })
        .unwrap_or_else(|| payment_attempt);

    let payment_attempt_fut = tokio::spawn(
        async move {
            Box::pin(async move {
                Ok::<_, error_stack::Report<errors::ApiErrorResponse>>(
                    match m_payment_attempt_update {
                        Some(payment_attempt_update) => m_db
                            .update_payment_attempt_with_attempt_id(
                                m_payment_attempt,
                                payment_attempt_update,
                                storage_scheme,
                            )
                            .await
                            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?,
                        None => m_payment_attempt,
                    },
                )
            })
            .await
        }
        .in_current_span(),
    );

    payment_data.payment_attempt = payment_attempt;

    payment_data.authentication = match payment_data.authentication {
        Some(mut authentication_store) => {
            let authentication_update = storage::AuthenticationUpdate::PostAuthorizationUpdate {
                authentication_lifecycle_status: enums::AuthenticationLifecycleStatus::Used,
            };
            let updated_authentication = state
                .store
                .update_authentication_by_merchant_id_authentication_id(
                    authentication_store.authentication,
                    authentication_update,
                )
                .await
                .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
            authentication_store.authentication = updated_authentication;
            Some(authentication_store)
        }
        None => None,
    };

    let amount_captured = get_total_amount_captured(
        &router_data.request,
        router_data.amount_captured.map(MinorUnit::new),
        router_data.status,
        &payment_data,
    );

    let payment_intent_update = match &router_data.response {
        Err(_) => storage::PaymentIntentUpdate::PGStatusUpdate {
            status: api_models::enums::IntentStatus::foreign_from(
                payment_data.payment_attempt.status,
            ),
            updated_by: storage_scheme.to_string(),
            // make this false only if initial payment fails, if incremental authorization call fails don't make it false
            incremental_authorization_allowed: Some(false),
            feature_metadata: payment_data
                .payment_intent
                .feature_metadata
                .clone()
                .map(masking::Secret::new),
        },
        Ok(_) => storage::PaymentIntentUpdate::ResponseUpdate {
            status: api_models::enums::IntentStatus::foreign_from(
                payment_data.payment_attempt.status,
            ),
            amount_captured,
            updated_by: storage_scheme.to_string(),
            fingerprint_id: payment_data.payment_attempt.fingerprint_id.clone(),
            incremental_authorization_allowed: payment_data
                .payment_intent
                .incremental_authorization_allowed,
            feature_metadata: payment_data
                .payment_intent
                .feature_metadata
                .clone()
                .map(masking::Secret::new),
        },
    };

    let m_db = state.clone().store;
    let m_key_store = key_store.clone();
    let m_payment_data_payment_intent = payment_data.payment_intent.clone();
    let m_payment_intent_update = payment_intent_update.clone();
    let key_manager_state: KeyManagerState = state.into();
    let payment_intent_fut = tokio::spawn(
        async move {
            m_db.update_payment_intent(
                &key_manager_state,
                m_payment_data_payment_intent,
                m_payment_intent_update,
                &m_key_store,
                storage_scheme,
            )
            .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound))
            .await
        }
        .in_current_span(),
    );

    // When connector requires redirection for mandate creation it can update the connector mandate_id during Psync and CompleteAuthorize
    let m_db = state.clone().store;
    let m_router_data_merchant_id = router_data.merchant_id.clone();
    let m_payment_method_id = payment_data.payment_attempt.payment_method_id.clone();
    let m_payment_data_mandate_id =
        payment_data
            .payment_attempt
            .mandate_id
            .clone()
            .or(payment_data
                .mandate_id
                .clone()
                .and_then(|mandate_ids| mandate_ids.mandate_id));
    let m_router_data_response = router_data.response.clone();
    let mandate_update_fut = tokio::spawn(
        async move {
            mandate::update_connector_mandate_id(
                m_db.as_ref(),
                &m_router_data_merchant_id,
                m_payment_data_mandate_id,
                m_payment_method_id,
                m_router_data_response,
                storage_scheme,
            )
            .await
        }
        .in_current_span(),
    );

    let (payment_intent, _, payment_attempt) = futures::try_join!(
        utils::flatten_join_error(payment_intent_fut),


pub async fn customers_delete(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<id_type::GlobalCustomerId>,
) -> impl Responder {
    let flow = Flow::CustomersDelete;
    let id = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        id,
        |state, auth: auth::AuthenticationData, id, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            delete_customer(state, merchant_context, id)
        },
        auth::auth_type(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::JWTAuth {
                permission: Permission::MerchantCustomerWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


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

use api_models::payments::ProxyPaymentsRequest;
use async_trait::async_trait;
use common_enums::enums;
use common_utils::types::keymanager::ToEncryptable;
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData, payments::PaymentConfirmData,
};
use hyperswitch_interfaces::api::ConnectorSpecifications;
use masking::PeekInterface;
use router_env::{instrument, tracing};

use super::{Domain, GetTracker, Operation, PostUpdateTracker, UpdateTracker, ValidateRequest};
use crate::{
    core::{
        errors::{self, CustomResult, RouterResult, StorageErrorExt},
        payments::{
            operations::{self, ValidateStatusForOperation},
            OperationSessionGetters, OperationSessionSetters,
        },
    },
    routes::{app::ReqState, SessionState},
    types::{
        self,
        api::{self, ConnectorCallType},
        domain::{self, types as domain_types},
        storage::{self, enums as storage_enums},
    },
    utils::OptionExt,
};

#[derive(Debug, Clone, Copy)]
pub struct PaymentProxyIntent;

impl ValidateStatusForOperation for PaymentProxyIntent {
    /// Validate if the current operation can be performed on the current status of the payment intent
    fn validate_status_for_operation(
        &self,
        intent_status: common_enums::IntentStatus,
    ) -> Result<(), errors::ApiErrorResponse> {
        match intent_status {
            //Failed state is included here so that in PCR, retries can be done for failed payments, otherwise for a failed attempt it was asking for new payment_intent
            common_enums::IntentStatus::RequiresPaymentMethod
            | common_enums::IntentStatus::Failed
            | common_enums::IntentStatus::Processing => Ok(()),
            //Failed state is included here so that in PCR, retries can be done for failed payments, otherwise for a failed attempt it was asking for new payment_intent
            common_enums::IntentStatus::RequiresPaymentMethod
            | common_enums::IntentStatus::Failed => Ok(()),
            common_enums::IntentStatus::Conflicted
            | common_enums::IntentStatus::Succeeded
            | common_enums::IntentStatus::Cancelled
            | common_enums::IntentStatus::CancelledPostCapture
            | common_enums::IntentStatus::RequiresCustomerAction
            | common_enums::IntentStatus::RequiresMerchantAction
            | common_enums::IntentStatus::RequiresCapture
            | common_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture
            | common_enums::IntentStatus::PartiallyCaptured
            | common_enums::IntentStatus::RequiresConfirmation
            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
            | common_enums::IntentStatus::Expired => {
                Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                    current_flow: format!("{self:?}"),
                    field_name: "status".to_string(),
                    current_value: intent_status.to_string(),
                    states: ["requires_payment_method".to_string()].join(", "),
                })
            }
        }
    }
}
type BoxedConfirmOperation<'b, F> =
    super::BoxedOperation<'b, F, ProxyPaymentsRequest, PaymentConfirmData<F>>;

impl<F: Send + Clone + Sync> Operation<F, ProxyPaymentsRequest> for &PaymentProxyIntent {
    type Data = PaymentConfirmData<F>;
    fn to_validate_request(
        &self,
    ) -> RouterResult<&(dyn ValidateRequest<F, ProxyPaymentsRequest, Self::Data> + Send + Sync)>
    {
        Ok(*self)
    }
    fn to_get_tracker(
        &self,
    ) -> RouterResult<&(dyn GetTracker<F, Self::Data, ProxyPaymentsRequest> + Send + Sync)> {
        Ok(*self)
    }
    fn to_domain(&self) -> RouterResult<&(dyn Domain<F, ProxyPaymentsRequest, Self::Data>)> {
        Ok(*self)
    }
    fn to_update_tracker(
        &self,
    ) -> RouterResult<&(dyn UpdateTracker<F, Self::Data, ProxyPaymentsRequest> + Send + Sync)> {
        Ok(*self)
    }
}

#[automatically_derived]
impl<F: Send + Clone + Sync> Operation<F, ProxyPaymentsRequest> for PaymentProxyIntent {
    type Data = PaymentConfirmData<F>;
    fn to_validate_request(
        &self,
    ) -> RouterResult<&(dyn ValidateRequest<F, ProxyPaymentsRequest, Self::Data> + Send + Sync)>
    {
        Ok(self)
    }
    fn to_get_tracker(
        &self,
    ) -> RouterResult<&(dyn GetTracker<F, Self::Data, ProxyPaymentsRequest> + Send + Sync)> {
        Ok(self)
    }
    fn to_domain(&self) -> RouterResult<&dyn Domain<F, ProxyPaymentsRequest, Self::Data>> {
        Ok(self)
    }
    fn to_update_tracker(
        &self,
    ) -> RouterResult<&(dyn UpdateTracker<F, Self::Data, ProxyPaymentsRequest> + Send + Sync)> {
        Ok(self)
    }
}

impl<F: Send + Clone + Sync> ValidateRequest<F, ProxyPaymentsRequest, PaymentConfirmData<F>>
    for PaymentProxyIntent
{
    #[instrument(skip_all)]
    fn validate_request<'a, 'b>(
        &'b self,
        _request: &ProxyPaymentsRequest,
        merchant_context: &'a domain::MerchantContext,
    ) -> RouterResult<operations::ValidateResult> {
        let validate_result = operations::ValidateResult {
            merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
            storage_scheme: merchant_context.get_merchant_account().storage_scheme,
            requeue: false,
        };

        Ok(validate_result)
    }
}

#[async_trait]
impl<F: Send + Clone + Sync> GetTracker<F, PaymentConfirmData<F>, ProxyPaymentsRequest>
    for PaymentProxyIntent
{
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &common_utils::id_type::GlobalPaymentId,
        request: &ProxyPaymentsRequest,
        merchant_context: &domain::MerchantContext,
        _profile: &domain::Profile,
        header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<operations::GetTrackerResponse<PaymentConfirmData<F>>> {
        let db = &*state.store;
        let key_manager_state = &state.into();

        let storage_scheme = merchant_context.get_merchant_account().storage_scheme;

        let payment_intent = db
            .find_payment_intent_by_id(
                key_manager_state,
                payment_id,
                merchant_context.get_merchant_key_store(),
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

        self.validate_status_for_operation(payment_intent.status)?;

        let cell_id = state.conf.cell_information.id.clone();

        let batch_encrypted_data = domain_types::crypto_operation(
            key_manager_state,
            common_utils::type_name!(hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt),
            domain_types::CryptoOperation::BatchEncrypt(
                hyperswitch_domain_models::payments::payment_attempt::FromRequestEncryptablePaymentAttempt::to_encryptable(
                    hyperswitch_domain_models::payments::payment_attempt::FromRequestEncryptablePaymentAttempt {
                        payment_method_billing_address: None,
                    },
                ),
            ),
            common_utils::types::keymanager::Identifier::Merchant(merchant_context.get_merchant_account().get_id().to_owned()),
            merchant_context.get_merchant_key_store().key.peek(),
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while encrypting payment intent details".to_string())?;

        let encrypted_data =
             hyperswitch_domain_models::payments::payment_attempt::FromRequestEncryptablePaymentAttempt::from_encryptable(batch_encrypted_data)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed while encrypting payment intent details")?;

        let payment_attempt = match payment_intent.active_attempt_id.clone() {
            Some(ref active_attempt_id) => db
                .find_payment_attempt_by_id(
                    key_manager_state,
                    merchant_context.get_merchant_key_store(),
                    active_attempt_id,
                    storage_scheme,
                )
                .await
                .change_context(errors::ApiErrorResponse::PaymentNotFound)
                .attach_printable("Could not find payment attempt")?,

            None => {
                let payment_attempt_domain_model: hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt =
                hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt::proxy_create_domain_model(
                    &payment_intent,
                    cell_id,
                    storage_scheme,
                    request,
                    encrypted_data
                )
                .await?;
                db.insert_payment_attempt(
                    key_manager_state,
                    merchant_context.get_merchant_key_store(),
                    payment_attempt_domain_model,
                    storage_scheme,
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Could not insert payment attempt")?
            }
        };

        let processor_payment_token = request.recurring_details.processor_payment_token.clone();

        let payment_address = hyperswitch_domain_models::payment_address::PaymentAddress::new(
            payment_intent
                .shipping_address
                .clone()
                .map(|address| address.into_inner()),
            payment_intent
                .billing_address
                .clone()
                .map(|address| address.into_inner()),
            payment_attempt
                .payment_method_billing_address
                .clone()
                .map(|address| address.into_inner()),
            Some(true),
        );
        let mandate_data_input = api_models::payments::MandateIds {
            mandate_id: None,
            mandate_reference_id: Some(
                api_models::payments::MandateReferenceId::ConnectorMandateId(
                    api_models::payments::ConnectorMandateReferenceId::new(
                        Some(processor_payment_token),
                        None,
                        None,
                        None,
                        None,
                    ),
                ),
            ),
        };

        let payment_data = PaymentConfirmData {
            flow: std::marker::PhantomData,
            payment_intent,
            payment_attempt,
            payment_method_data: Some(PaymentMethodData::MandatePayment),
            payment_address,
            mandate_data: Some(mandate_data_input),
            payment_method: None,
            merchant_connector_details: None,
            external_vault_pmd: None,
        };

        let get_trackers_response = operations::GetTrackerResponse { payment_data };

        Ok(get_trackers_response)
    }
}

#[async_trait]
impl<F: Clone + Send + Sync> Domain<F, ProxyPaymentsRequest, PaymentConfirmData<F>>
    for PaymentProxyIntent
{
    async fn get_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut PaymentConfirmData<F>,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: storage_enums::MerchantStorageScheme,
    ) -> CustomResult<(BoxedConfirmOperation<'a, F>, Option<domain::Customer>), errors::StorageError>
    {
        Ok((Box::new(self), None))
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut PaymentConfirmData<F>,
        _storage_scheme: storage_enums::MerchantStorageScheme,
        _key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedConfirmOperation<'a, F>,
        Option<PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }
    #[instrument(skip_all)]
    async fn populate_payment_data<'a>(
        &'a self,
        _state: &SessionState,
        payment_data: &mut PaymentConfirmData<F>,
        _merchant_context: &domain::MerchantContext,
        _business_profile: &domain::Profile,
        connector_data: &api::ConnectorData,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        let connector_request_reference_id = connector_data
            .connector
            .generate_connector_request_reference_id(
                &payment_data.payment_intent,
                &payment_data.payment_attempt,
            );
        payment_data.set_connector_request_reference_id(Some(connector_request_reference_id));
        Ok(())
    }

    async fn perform_routing<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        _business_profile: &domain::Profile,
        state: &SessionState,
        payment_data: &mut PaymentConfirmData<F>,
    ) -> CustomResult<ConnectorCallType, errors::ApiErrorResponse> {
        let connector_name = payment_data.get_payment_attempt_connector();
        if let Some(connector_name) = connector_name {
            let merchant_connector_id = payment_data.get_merchant_connector_id_in_attempt();
            let connector_data = api::ConnectorData::get_connector_by_name(
                &state.conf.connectors,
                connector_name,
                api::GetToken::Connector,
                merchant_connector_id,
            )?;

            Ok(ConnectorCallType::PreDetermined(connector_data.into()))
        } else {
            Err(error_stack::Report::new(
                errors::ApiErrorResponse::InternalServerError,
            ))
        }
    }
}
#[async_trait]
impl<F: Clone + Sync> UpdateTracker<F, PaymentConfirmData<F>, ProxyPaymentsRequest>
    for PaymentProxyIntent
{
    #[instrument(skip_all)]
    async fn update_trackers<'b>(
        &'b self,
        state: &'b SessionState,
        _req_state: ReqState,
        mut payment_data: PaymentConfirmData<F>,
        _customer: Option<domain::Customer>,
        storage_scheme: storage_enums::MerchantStorageScheme,
        _updated_customer: Option<storage::CustomerUpdate>,
        key_store: &domain::MerchantKeyStore,
        _frm_suggestion: Option<api_models::enums::FrmSuggestion>,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(BoxedConfirmOperation<'b, F>, PaymentConfirmData<F>)>
    where
        F: 'b + Send,
    {
        let db = &*state.store;
        let key_manager_state = &state.into();

        let intent_status = common_enums::IntentStatus::Processing;
        let attempt_status = common_enums::AttemptStatus::Pending;

        let connector = payment_data
            .payment_attempt
            .connector
            .clone()
            .get_required_value("connector")
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Connector is none when constructing response")?;

        let merchant_connector_id = Some(
            payment_data
                .payment_attempt
                .merchant_connector_id
                .clone()
                .get_required_value("merchant_connector_id")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Merchant connector id is none when constructing response")?,
        );

        let payment_intent_update =
            hyperswitch_domain_models::payments::payment_intent::PaymentIntentUpdate::ConfirmIntent {
                status: intent_status,
                updated_by: storage_scheme.to_string(),
                active_attempt_id: Some(payment_data.payment_attempt.id.clone()),
            };

        let authentication_type = payment_data
            .payment_intent
            .authentication_type
            .unwrap_or_default();

        let connector_request_reference_id = payment_data
            .payment_attempt
            .connector_request_reference_id
            .clone();

        let connector_response_reference_id = payment_data
            .payment_attempt
            .connector_response_reference_id
            .clone();

        let payment_attempt_update = hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptUpdate::ConfirmIntent {
            status: attempt_status,
            updated_by: storage_scheme.to_string(),
            connector,
            merchant_connector_id,
            authentication_type,
            connector_request_reference_id,
            connector_response_reference_id,
        };

        let updated_payment_intent = db
            .update_payment_intent(
                key_manager_state,
                payment_data.payment_intent.clone(),
                payment_intent_update,
                key_store,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment intent")?;

        payment_data.payment_intent = updated_payment_intent;

        let updated_payment_attempt = db
            .update_payment_attempt(
                key_manager_state,
                key_store,
                payment_data.payment_attempt.clone(),
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment attempt")?;

        payment_data.payment_attempt = updated_payment_attempt;

        Ok((Box::new(self), payment_data))
    }
}

#[cfg(feature = "v2")]
#[async_trait]
impl<F: Clone> PostUpdateTracker<F, PaymentConfirmData<F>, types::PaymentsAuthorizeData>
    for PaymentProxyIntent
{
    async fn update_tracker<'b>(
        &'b self,
        state: &'b SessionState,
        mut payment_data: PaymentConfirmData<F>,
        response: types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<PaymentConfirmData<F>>
    where
        F: 'b + Send + Sync,
        types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>:
            hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<
                F,
                types::PaymentsAuthorizeData,
                PaymentConfirmData<F>,
            >,
    {
        use hyperswitch_domain_models::router_data::TrackerPostUpdateObjects;

        let db = &*state.store;
        let key_manager_state = &state.into();

        let response_router_data = response;

        let payment_intent_update =
            response_router_data.get_payment_intent_update(&payment_data, storage_scheme);
        let payment_attempt_update =
            response_router_data.get_payment_attempt_update(&payment_data, storage_scheme);

        let updated_payment_intent = db
            .update_payment_intent(
                key_manager_state,
                payment_data.payment_intent,
                payment_intent_update,
                key_store,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment intent")?;

        let updated_payment_attempt = db
            .update_payment_attempt(
                key_manager_state,
                key_store,
                payment_data.payment_attempt,
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to update payment attempt")?;

        payment_data.payment_intent = updated_payment_intent;
        payment_data.payment_attempt = updated_payment_attempt;

        Ok(payment_data)
    }
}


pub struct CardResponseObject {
    suffix: Option<String>,
    prefix: Option<String>,
    expiration_month: Option<Secret<String>>,
    expiration_year: Option<Secret<String>>,
    #[serde(rename = "type")]
    card_type: Option<String>,
}


pub struct DummyConnectorCard {
    name: Secret<String>,
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
}


pub struct Amount {
    pub amount: StringMajorUnit,
    pub currency: String,
}


pub struct PaymentMethods;


pub struct ProfileLevel {
    pub tenant_id: id_type::TenantId,
    pub org_id: id_type::OrganizationId,
    pub merchant_id: id_type::MerchantId,
    pub profile_id: id_type::ProfileId,
}


File: crates/router/tests/connectors/opayo.rs

use std::str::FromStr;

use masking::Secret;
use router::types::{self, domain, storage::enums};

use crate::{
    connector_auth,
    utils::{self, ConnectorActions},
};

#[derive(Clone, Copy)]
struct OpayoTest;
impl ConnectorActions for OpayoTest {}
impl utils::Connector for OpayoTest {
    fn get_data(&self) -> types::api::ConnectorData {
        use router::connector::Opayo;
        utils::construct_connector_data_old(
            Box::new(Opayo::new()),
            // Remove `dummy_connector` feature gate from module in `main.rs` when updating this to use actual connector variant
            types::Connector::DummyConnector1,
            types::api::GetToken::Connector,
            None,
        )
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .opayo
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }

    fn get_name(&self) -> String {
        "opayo".to_string()
    }
}

static CONNECTOR: OpayoTest = OpayoTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect card number.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_card_number() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_number: cards::CardNumber::from_str("1234567891011").unwrap(),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card number is incorrect.".to_string(),
    );
}

// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: domain::PaymentMethodData::Card(domain::Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests


impl GlobalTokenId {
    /// Get string representation of the id
    pub fn get_string_repr(&self) -> &str {
        self.0.get_string_repr()
    }

    ///Get GlobalTokenId from a string
    pub fn from_string(token_string: &str) -> CustomResult<Self, ValidationError> {
        let token = super::GlobalId::from_string(Cow::Owned(token_string.to_string()))
            .change_context(ValidationError::IncorrectValueProvided {
                field_name: "GlobalTokenId",
            })?;
        Ok(Self(token))
    }

    /// Generate a new GlobalTokenId from a cell id
    pub fn generate(cell_id: &crate::id_type::CellId) -> Self {
        let global_id = super::GlobalId::generate(cell_id, super::GlobalEntity::Token);
        Self(global_id)
    }
}


pub struct Server {
    pub port: u16,
    pub workers: usize,
    pub host: String,
}


OpenAPI Block Path: components.schemas.PaymentChargeType
{
  "oneOf": [
    {
      "type": "object",
      "required": [
        "Stripe"
      ],
      "properties": {
        "Stripe": {
          "$ref": "#/components/schemas/StripeChargeType"
        }
      }
    }
  ]
}


pub async fn update_payment_method_status_internal(
    state: &SessionState,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: enums::MerchantStorageScheme,
    status: enums::PaymentMethodStatus,
    payment_method_id: &id_type::GlobalPaymentMethodId,
) -> RouterResult<domain::PaymentMethod> {
    let db = &*state.store;
    let key_manager_state = &state.into();

    let payment_method = db
        .find_payment_method(
            &((state).into()),
            key_store,
            payment_method_id,
            storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

    let pm_update = storage::PaymentMethodUpdate::StatusUpdate {
        status: Some(status),
    };

    let updated_pm = db
        .update_payment_method(
            key_manager_state,
            key_store,
            payment_method.clone(),
            pm_update,
            storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update payment method in db")?;

    Ok(updated_pm)
}


pub fn new() -> &'static Self {
        &Self {
            amount_convertor: &StringMajorUnitForConnector,
        }
    }


impl api::MandateSetup for Hipay {}


pub struct EphemeralKeyNew {
    pub id: String,
    pub merchant_id: common_utils::id_type::MerchantId,
    pub customer_id: common_utils::id_type::CustomerId,
    pub secret: String,
}


pub struct Session;


pub struct FulfilmentData {
    fulfillment_id: String,
    #[serde(with = "common_utils::custom_serde::iso8601")]
    created_at: PrimitiveDateTime,
    status: Option<FulfillmentRequestStatus>,
    tracking_company: String,
    tracking_number: String,
    tracking_url: Option<String>,
}


pub async fn send_verification_mail(
    state: SessionState,
    req: user_api::SendVerifyEmailRequest,
    auth_id: Option<String>,
    theme_id: Option<String>,
) -> UserResponse<()> {
    let user_email = domain::UserEmail::from_pii_email(req.email)?;

    utils::user::validate_email_domain_auth_type_using_db(
        &state,
        &user_email,
        UserAuthType::MagicLink,
    )
    .await?;

    let user = state
        .global_store
        .find_user_by_email(&user_email)
        .await
        .map_err(|e| {
            if e.current_context().is_db_not_found() {
                e.change_context(UserErrors::UserNotFound)
            } else {
                e.change_context(UserErrors::InternalServerError)
            }
        })?;

    if user.is_verified {
        return Err(UserErrors::UserAlreadyVerified.into());
    }

    let theme = theme_utils::get_theme_using_optional_theme_id(&state, theme_id).await?;

    let email_contents = email_types::VerifyEmail {
        recipient_email: domain::UserEmail::from_pii_email(user.email)?,
        settings: state.conf.clone(),
        auth_id,
        theme_id: theme.as_ref().map(|theme| theme.theme_id.clone()),
        theme_config: theme
            .map(|theme| theme.email_config())
            .unwrap_or(state.conf.theme.email_config.clone()),
    };

    state
        .email_client
        .compose_and_send_email(
            user_utils::get_base_url(&state),
            Box::new(email_contents),
            state.conf.proxy.https_url.as_ref(),
        )
        .await
        .change_context(UserErrors::InternalServerError)?;

    Ok(ApplicationResponse::StatusOk)
}


/// Extracts the inner value from the wrapper
        pub fn into_inner(self) -> T {
            self.0
        }


OpenAPI Block Path: components.schemas.RefundListRequest
{
  "allOf": [
    {
      "allOf": [
        {
          "$ref": "#/components/schemas/TimeRange"
        }
      ],
      "nullable": true
    },
    {
      "type": "object",
      "properties": {
        "payment_id": {
          "type": "string",
          "description": "The identifier for the payment",
          "nullable": true
        },
        "refund_id": {
          "type": "string",
          "description": "The identifier for the refund",
          "nullable": true
        },
        "profile_id": {
          "type": "string",
          "description": "The identifier for business profile",
          "nullable": true
        },
        "limit": {
          "type": "integer",
          "format": "int64",
          "description": "Limit on the number of objects to return",
          "nullable": true
        },
        "offset": {
          "type": "integer",
          "format": "int64",
          "description": "The starting point within a list of objects",
          "nullable": true
        },
        "amount_filter": {
          "allOf": [
            {
              "$ref": "#/components/schemas/AmountFilter"
            }
          ],
          "nullable": true
        },
        "connector": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The list of connectors to filter refunds list",
          "nullable": true
        },
        "merchant_connector_id": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The list of merchant connector ids to filter the refunds list for selected label",
          "nullable": true
        },
        "currency": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Currency"
          },
          "description": "The list of currencies to filter refunds list",
          "nullable": true
        },
        "refund_status": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/RefundStatus"
          },
          "description": "The list of refund statuses to filter refunds list",
          "nullable": true
        }
      }
    }
  ]
}